This notebook contains exploratory analyses of behavioral data collected to investigate the relationship between risk taking behavior and probabilistic learning.

The sample consists of three age groups: kids, teens and adults and we hypothesize that sensitivity to learn from high variance feedback improves with age (and this is related to better risky decisions).

Subjects completed a probabilistic learning task in the scanner, a risky decision making task (BART) outside the scanner and numerous questionnaires. The focus of this notebook is on the first task.

The plan of analysis is to establish that adults are more sensitive to high variance feedback in the probabilistic learning task and relate this (modeled) sensitivity to behavior in BART.

Sample info

First let’s get a sense of the sample. Here is how many subjects we have who have complete datasets for the probabilistic learning task and their age break downs.

machine_game_data_clean %>% 
  group_by(age_group) %>%
  summarise(min_age = min(calc_age),
            mean_age = mean(calc_age),
            sd_age = sd(calc_age),
            max_age = max(calc_age),
            n = n()/180)

Performance in RL task

In this task subjects are presented with a fractal in each trial. The fractals represent different machines (single-armed bandits). Subjects choose to play or pass in each trial. Each machine yields a probabilistic reward. There are four machines in total. Two with positive and two with negative expected value. One of each of these machines has a low variance reward schedule while the other has a high variance reward schedule.

  • One machine gives $5 90% of the time and -$495 %10 of the time
  • One machine gives -$5 90% of the time and $495 %10 of the time
  • One machine gives $10 50% of the time and -$100 %50 of the time
  • One machine gives -$10 50% of the time and $100 %50 of the time

Points earned

Performance in this task can be assessed by looking at the total number of points subjects make at the end of task. The following graph shows that adults collect more points in this task compared to kids.

machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  summarise(total_points = sum(Points_earned)) %>%
  do(assign.age.info(.)) %>%
  group_by(age_group) %>%
  summarise(mean_points = mean(total_points),
            sem_points = sem(total_points)) %>%
  ggplot(aes(age_group, mean_points))+
  geom_bar(stat='identity', position = position_dodge((0.9)))+
  geom_errorbar(aes(ymin=mean_points-sem_points, ymax=mean_points+sem_points), position = position_dodge(0.9), width=0.25)+
  theme_bw()+
  xlab('Machine')+
  ylab('Mean points')+
  labs(fill='Age group')

This difference is statistically significant: adults earn more points compared to the kids.

tmp = machine_game_data_clean %>%
  group_by(Sub_id) %>%
  summarise(total_points = sum(Points_earned)) %>%
  do(assign.age.info(.))

summary(lm(total_points~age_group, data=tmp))

Call:
lm(formula = total_points ~ age_group, data = tmp)

Residuals:
    Min      1Q  Median      3Q     Max 
-2820.0  -957.6  -203.5  1131.9  2416.5 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)   
(Intercept)       277.6      248.1   1.119   0.2670   
age_groupteen     525.9      408.2   1.289   0.2017   
age_groupadult    972.4      350.9   2.771   0.0071 **
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1336 on 72 degrees of freedom
Multiple R-squared:  0.09651,   Adjusted R-squared:  0.07141 
F-statistic: 3.845 on 2 and 72 DF,  p-value: 0.0259

Since we are interested in the age differences between sensitivity to different feedback schedules, we should show that this difference in performance exists especially for the high variance feedback condition(s). Here is the plot of performance (total points earned) broken down by conditions.

machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  summarise(total_points = sum(Points_earned)) %>%
  do(assign.age.info(.)) %>%
  group_by(age_group, facet_labels) %>%
  summarise(mean_points = mean(total_points),
            sem_points = sem(total_points)) %>%
  ggplot(aes(facet_labels, mean_points, fill=age_group))+
  geom_bar(stat='identity', position = position_dodge((0.9)))+
  geom_errorbar(aes(ymin=mean_points-sem_points, ymax=mean_points+sem_points), position = position_dodge(0.9), width=0.25)+
  # theme_bw()+
  xlab('Machine')+
  ylab('Mean points')+
  labs(fill='Age group')

ggsave("Points_earned.jpeg", device = "jpeg", path = fig_path, width = 7, height = 5, units = "in", dpi = 450)

Running separate models for positive and negative EV machines for ease of interpretation.

tmp <- machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  summarise(total_points = sum(Points_earned)) %>%
  do(assign.age.info(.))

In the positive EV machines there is a main effect for the high variance machine. Subjects earn fewer points in the high variance condition compared to the low variance condition. There are no age differences.

summary(lm(total_points ~ age_group*facet_labels, data = tmp %>% filter(facet_labels %in% c("-10,+100", "-5,+495"))))

Call:
lm(formula = total_points ~ age_group * facet_labels, data = tmp %>% 
    filter(facet_labels %in% c("-10,+100", "-5,+495")))

Residuals:
     Min       1Q   Median       3Q      Max 
-1305.17  -354.12    64.83   432.46   989.83 

Coefficients:
                                   Estimate Std. Error t value Pr(>|t|)
(Intercept)                         1475.17      88.98  16.580   <2e-16
age_groupteen                        208.95     146.36   1.428   0.1556
age_groupadult                       215.86     125.83   1.716   0.0884
facet_labels-5,+495                 -293.62     125.83  -2.333   0.0210
age_groupteen:facet_labels-5,+495    -40.50     206.99  -0.196   0.8452
age_groupadult:facet_labels-5,+495  -112.24     177.95  -0.631   0.5292
                                      
(Intercept)                        ***
age_groupteen                         
age_groupadult                     .  
facet_labels-5,+495                *  
age_groupteen:facet_labels-5,+495     
age_groupadult:facet_labels-5,+495    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 479.1 on 144 degrees of freedom
Multiple R-squared:  0.1457,    Adjusted R-squared:  0.116 
F-statistic: 4.912 on 5 and 144 DF,  p-value: 0.0003511

In the negative EV machines there is again a main effect for the high variance machine: Everyone losses fewer points in the low variance condition. There is also a main effect for adults: Adults perform better than kids for both negative EV machines.

summary(lm(total_points ~ age_group*facet_labels, data = tmp %>% filter(facet_labels %in% c("+10,-100", "+5,-495"))))

Call:
lm(formula = total_points ~ age_group * facet_labels, data = tmp %>% 
    filter(facet_labels %in% c("+10,-100", "+5,-495")))

Residuals:
     Min       1Q   Median       3Q      Max 
-1236.21  -388.79    33.79   387.24  1000.34 

Coefficients:
                                   Estimate Std. Error t value Pr(>|t|)
(Intercept)                         -953.79      89.33 -10.677  < 2e-16
age_groupteen                         59.68     146.95   0.406 0.685276
age_groupadult                       296.55     126.34   2.347 0.020271
facet_labels+5,-495                 -471.55     126.34  -3.733 0.000272
age_groupteen:facet_labels+5,-495     29.20     207.82   0.141 0.888460
age_groupadult:facet_labels+5,-495    59.83     178.67   0.335 0.738222
                                      
(Intercept)                        ***
age_groupteen                         
age_groupadult                     *  
facet_labels+5,-495                ***
age_groupteen:facet_labels+5,-495     
age_groupadult:facet_labels+5,-495    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 481.1 on 144 degrees of freedom
Multiple R-squared:  0.2421,    Adjusted R-squared:  0.2158 
F-statistic: 9.199 on 5 and 144 DF,  p-value: 1.289e-07

So the age diffence in performance is driven by difference in performance in negative EV machines. The question is what difference in behavior in these conditions is leading to this difference in performance?

To anticipate possible cognitive processes that will be parameterized in RL models differences can lie in: how quickly the groups learn the probabilities, how much weight they put on the outcomes and/or how much like an optimal agent they behave.

Proportion of playing

The first thing we can look at is how often subjects play versus pass. It’s hard to see any age differences when we just look at frequency of overall playing as below.

machine_game_data_clean %>%
  group_by(Sub_id, Response) %>%
  tally %>%
  group_by(Sub_id) %>%
  mutate(pct=(100*n)/sum(n)) %>%
  do(assign.age.info(.)) %>%
  group_by(age_group, Response) %>%
  dplyr::summarise(mean_pct = mean(pct),
            sem_pct = sem(pct)) %>%
  ggplot(aes(Response, mean_pct, fill = age_group))+
  geom_bar(stat='identity', position = position_dodge(0.9))+
  geom_errorbar(aes(ymin = mean_pct - sem_pct, ymax = mean_pct + sem_pct), position = position_dodge(width = 0.9), width=0.25)+
  theme_bw()+
  ylab('Percentage of trials')+
  labs(fill = 'Age group')

It is also not immediately apparent how to translate this to better performance/learning in this task but one way to think about it: If people learned perfectly they should play half of the time (always for the positive expected value trial and never for the negative expected value trials). The fact that all play proportions are above 50% suggests that nobody learns perfectly and that adults might be closest to it. But this is very crude and a better way to look at it would be to see

  1. how this depends on the different machines and
  2. how it changes throughout the task.

To get a better sense of overall behavior in different contingency states we break this proportion of playing down by machines.

Now we can see age differences in playing frequency in different conditions, particularly in the negative expected value machines (bottom row).

machine_game_data_clean %>%
  group_by(Sub_id, facet_labels, Response) %>%
  tally %>%
  group_by(Sub_id, facet_labels) %>%
  mutate(pct=(100*n)/sum(n)) %>%
  do(assign.age.info(.)) %>%
  group_by(age_group, facet_labels, Response) %>%
  dplyr::summarise(mean_pct = mean(pct),
            sem_pct = sem(pct)) %>%
  ggplot(aes(Response, mean_pct, fill = age_group))+
  geom_bar(stat='identity', position = position_dodge(0.9))+
  geom_errorbar(aes(ymin = mean_pct - sem_pct, ymax = mean_pct + sem_pct), position = position_dodge(width = 0.9), width=0.25)+
  theme_bw()+
  ylab('Percentage of trials')+
  facet_wrap(~facet_labels)+
  labs(fill = 'Age group')

The differences in points earned map directly on to proportion of choosing to play each machine:

  • Adults play less than kids for both negative EV machines.
  • Everyone plays the high var positive EV machine less than the low var positive EV machine.
  • Everyone plays the low var negative EV machines less than the low var positive EV machine.
tmp <- machine_game_data_clean %>%
  group_by(Sub_id, facet_labels, Response) %>%
  tally %>%
  group_by(Sub_id, facet_labels) %>%
  mutate(pct_play=(100*n)/sum(n)) %>%
  filter(Response == 'play') %>%
  do(assign.age.info(.))

summary(lmer(pct_play ~ age_group*facet_labels + (1|Sub_id), data = tmp))
Linear mixed model fit by REML ['lmerMod']
Formula: pct_play ~ age_group * facet_labels + (1 | Sub_id)
   Data: tmp

REML criterion at convergence: 2642.6

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.65914 -0.69208  0.03646  0.75004  1.97139 

Random effects:
 Groups   Name        Variance Std.Dev.
 Sub_id   (Intercept)  50.81    7.128  
 Residual             451.18   21.241  
Number of obs: 300, groups:  Sub_id, 75

Fixed effects:
                                    Estimate Std. Error t value
(Intercept)                          74.2529     4.1605  17.847
age_groupteen                        10.1916     6.8439   1.489
age_groupadult                        9.6103     5.8839   1.633
facet_labels-5,+495                 -17.0115     5.5782  -3.050
facet_labels+10,-100                -28.2759     5.5782  -5.069
facet_labels+5,-495                 -10.4215     5.5782  -1.868
age_groupteen:facet_labels-5,+495    -0.7663     9.1758  -0.084
age_groupadult:facet_labels-5,+495   -1.7751     7.8887  -0.225
age_groupteen:facet_labels+10,-100  -13.2928     9.1758  -1.449
age_groupadult:facet_labels+10,-100 -23.7866     7.8887  -3.015
age_groupteen:facet_labels+5,-495   -14.4151     9.1758  -1.571
age_groupadult:facet_labels+5,-495  -27.7457     7.8887  -3.517

Correlation of Fixed Effects:
                  (Intr) ag_grpt ag_grpd f_-5,+ f_+10, f_+5,-
age_grouptn       -0.608                                     
age_gropdlt       -0.707  0.430                              
fct_-5,+495       -0.670  0.408   0.474                      
fc_+10,-100       -0.670  0.408   0.474   0.500              
fct_+5,-495       -0.670  0.408   0.474   0.500  0.500       
ag_grpt:_-5,+495   0.408 -0.670  -0.288  -0.608 -0.304 -0.304
ag_grpd:_-5,+495   0.474 -0.288  -0.670  -0.707 -0.354 -0.354
ag_grpt:_+10,-100  0.408 -0.670  -0.288  -0.304 -0.608 -0.304
ag_grpd:_+10,-100  0.474 -0.288  -0.670  -0.354 -0.707 -0.354
ag_grpt:_+5,-495   0.408 -0.670  -0.288  -0.304 -0.304 -0.608
ag_grpd:_+5,-495   0.474 -0.288  -0.670  -0.354 -0.354 -0.707
                  ag_grpt:_-5,+495 ag_grpd:_-5,+495 ag_grpt:_+10,-100
age_grouptn                                                          
age_gropdlt                                                          
fct_-5,+495                                                          
fc_+10,-100                                                          
fct_+5,-495                                                          
ag_grpt:_-5,+495                                                     
ag_grpd:_-5,+495   0.430                                             
ag_grpt:_+10,-100  0.500            0.215                            
ag_grpd:_+10,-100  0.215            0.500            0.430           
ag_grpt:_+5,-495   0.500            0.215            0.500           
ag_grpd:_+5,-495   0.215            0.500            0.215           
                  ag_grpd:_+10,-100 ag_grpt:_+5,-495
age_grouptn                                         
age_gropdlt                                         
fct_-5,+495                                         
fc_+10,-100                                         
fct_+5,-495                                         
ag_grpt:_-5,+495                                    
ag_grpd:_-5,+495                                    
ag_grpt:_+10,-100                                   
ag_grpd:_+10,-100                                   
ag_grpt:_+5,-495   0.215                            
ag_grpd:_+5,-495   0.500             0.430          

This is not surprising given what the number of points earned already showed. But now that we are looking at a behavioral measure instead of an outcome measure we might be able to quantify constructs of interest like sensitivity to variance or sensitivity to the expected values of the machines.

As a first step to translate raw playing behavior to learning I recoded the choices to be correct when a subject chooses to play a positive expected value machine and pass a negative expected value machine and incorrect when the reverse is true. If a subject is learning they should be learning to play the positive expected machines and to pass the others.

Learning

Recoding the behavior in this way gave a clearer picture of the age difference in learning of optimal behavior between the conditions. Specifically we can now look at how the probability of a correct choice changes for each age group in each condition across trials.

machine_game_data_clean %>%
  ggplot(aes(scale(Trial_number), correct1_incorrect0))+
  geom_line(aes(group = Sub_id, col= factor(age_group, levels=c('kid', 'teen', 'adult'))),stat='smooth', method = 'glm', method.args = list(family = "binomial"), se = FALSE, alpha=0.2)+
  geom_line(aes(col= factor(age_group, levels=c('kid', 'teen', 'adult'))),stat='smooth', method = 'glm', method.args = list(family = "binomial"), se = FALSE, alpha=1, size=2)+
  facet_wrap(~facet_labels)+
  theme_bw()+
  xlab("Relative trial number")+
  scale_y_continuous(breaks=c(0,1))+
  labs(col="Age group")+
  ylab('Correct choice')+
  theme(legend.position = "bottom",
        panel.grid = element_blank())

ggsave("Learning.jpeg", device = "jpeg", path = fig_path, width = 8, height = 5, units = "in", dpi = 450)

Looking at learning effects separately for each machine to avoid interpreting messy three-way interactions.

Adults are more likely to make correct decisions in low var positive EV machine.

summary(glmer(correct1_incorrect0 ~ age_group*scale(Trial_number)+(1|Sub_id), data = machine_game_data_clean %>% filter(facet_labels %in% c('-10,+100')), family=binomial))
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: correct1_incorrect0 ~ age_group * scale(Trial_number) + (1 |  
    Sub_id)
   Data: 
machine_game_data_clean %>% filter(facet_labels %in% c("-10,+100"))

     AIC      BIC   logLik deviance df.resid 
  2802.7   2845.5  -1394.3   2788.7     3364 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.1092  0.1244  0.2237  0.4674  1.8848 

Random effects:
 Groups Name        Variance Std.Dev.
 Sub_id (Intercept) 2.437    1.561   
Number of obs: 3371, groups:  Sub_id, 75

Fixed effects:
                                    Estimate Std. Error z value Pr(>|z|)
(Intercept)                         1.436722   0.303684   4.731 2.23e-06
age_groupteen                       0.864371   0.508376   1.700   0.0891
age_groupadult                      1.091426   0.440513   2.478   0.0132
scale(Trial_number)                 0.007478   0.068963   0.108   0.9136
age_groupteen:scale(Trial_number)  -0.015514   0.126248  -0.123   0.9022
age_groupadult:scale(Trial_number)  0.103024   0.111180   0.927   0.3541
                                      
(Intercept)                        ***
age_groupteen                      .  
age_groupadult                     *  
scale(Trial_number)                   
age_groupteen:scale(Trial_number)     
age_groupadult:scale(Trial_number)    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
             (Intr) ag_grpt ag_grpd sc(T_) ag_grpt:(T_)
age_grouptn  -0.591                                    
age_gropdlt  -0.679  0.413                             
scl(Trl_nm)   0.001  0.000   0.000                     
ag_grpt:(T_)  0.000 -0.001   0.000  -0.546             
ag_grpd:(T_)  0.000  0.001   0.012  -0.620  0.339      

The probability of making a correct response for the high var positive EV machine doesn’t change for adults or kids but increases for teens across trials.

summary(glmer(correct1_incorrect0 ~ age_group*scale(Trial_number)+(1|Sub_id), data = machine_game_data_clean %>% filter(facet_labels %in% c('-5,+495')), family=binomial))
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: correct1_incorrect0 ~ age_group * scale(Trial_number) + (1 |  
    Sub_id)
   Data: 
machine_game_data_clean %>% filter(facet_labels %in% c("-5,+495"))

     AIC      BIC   logLik deviance df.resid 
  3596.7   3639.5  -1791.3   3582.7     3363 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-4.5183 -0.7139  0.2926  0.5768  3.5857 

Random effects:
 Groups Name        Variance Std.Dev.
 Sub_id (Intercept) 2.196    1.482   
Number of obs: 3370, groups:  Sub_id, 75

Fixed effects:
                                   Estimate Std. Error z value Pr(>|z|)  
(Intercept)                         0.42598    0.28422   1.499   0.1339  
age_groupteen                       0.53501    0.46878   1.141   0.2538  
age_groupadult                      0.53030    0.40470   1.310   0.1901  
scale(Trial_number)                 0.02285    0.06369   0.359   0.7198  
age_groupteen:scale(Trial_number)   0.25742    0.10980   2.345   0.0191 *
age_groupadult:scale(Trial_number)  0.01845    0.09517   0.194   0.8463  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
             (Intr) ag_grpt ag_grpd sc(T_) ag_grpt:(T_)
age_grouptn  -0.606                                    
age_gropdlt  -0.702  0.426                             
scl(Trl_nm)   0.000  0.000   0.000                     
ag_grpt:(T_)  0.000  0.013   0.001  -0.580             
ag_grpd:(T_)  0.000  0.000   0.002  -0.669  0.388      

All groups show improvement across trials for the low var negative EV machine but adults learn faster than kids and teens.

summary(glmer(correct1_incorrect0 ~ age_group*scale(Trial_number)+(1|Sub_id), data = machine_game_data_clean %>% filter(facet_labels %in% c('+10,-100')), family=binomial))
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: correct1_incorrect0 ~ age_group * scale(Trial_number) + (1 |  
    Sub_id)
   Data: 
machine_game_data_clean %>% filter(facet_labels %in% c("+10,-100"))

     AIC      BIC   logLik deviance df.resid 
  4004.9   4047.8  -1995.5   3990.9     3368 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-4.1747 -0.8618  0.3667  0.7401  3.7838 

Random effects:
 Groups Name        Variance Std.Dev.
 Sub_id (Intercept) 1.048    1.024   
Number of obs: 3375, groups:  Sub_id, 75

Fixed effects:
                                   Estimate Std. Error z value Pr(>|z|)
(Intercept)                        -0.13227    0.20125  -0.657 0.511016
age_groupteen                       0.47378    0.33003   1.436 0.151123
age_groupadult                      1.00065    0.28556   3.504 0.000458
scale(Trial_number)                 0.30159    0.06298   4.789 1.68e-06
age_groupteen:scale(Trial_number)  -0.01943    0.10144  -0.192 0.848077
age_groupadult:scale(Trial_number)  0.33528    0.09316   3.599 0.000319
                                      
(Intercept)                           
age_groupteen                         
age_groupadult                     ***
scale(Trial_number)                ***
age_groupteen:scale(Trial_number)     
age_groupadult:scale(Trial_number) ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
             (Intr) ag_grpt ag_grpd sc(T_) ag_grpt:(T_)
age_grouptn  -0.610                                    
age_gropdlt  -0.706  0.431                             
scl(Trl_nm)  -0.003  0.002   0.003                     
ag_grpt:(T_)  0.001  0.005  -0.001  -0.620             
ag_grpd:(T_)  0.000  0.000   0.032  -0.674  0.419      

Kids don’t show learning across trials for the high var negative EV machine but adults and teens do.

summary(glmer(correct1_incorrect0 ~ age_group*scale(Trial_number)+(1|Sub_id), data = machine_game_data_clean%>% filter(facet_labels %in% c('+5,-495')), family=binomial))
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: correct1_incorrect0 ~ age_group * scale(Trial_number) + (1 |  
    Sub_id)
   Data: 
machine_game_data_clean %>% filter(facet_labels %in% c("+5,-495"))

     AIC      BIC   logLik deviance df.resid 
  3873.0   3915.9  -1929.5   3859.0     3377 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.5639 -0.6775 -0.3712  0.7531  3.1844 

Random effects:
 Groups Name        Variance Std.Dev.
 Sub_id (Intercept) 1.207    1.099   
Number of obs: 3384, groups:  Sub_id, 75

Fixed effects:
                                   Estimate Std. Error z value Pr(>|z|)
(Intercept)                        -0.92874    0.21603  -4.299 1.71e-05
age_groupteen                       0.38389    0.35357   1.086 0.277582
age_groupadult                      1.06277    0.30440   3.491 0.000481
scale(Trial_number)                 0.02308    0.06472   0.357 0.721394
age_groupteen:scale(Trial_number)   0.39678    0.10592   3.746 0.000180
age_groupadult:scale(Trial_number)  0.70453    0.09512   7.407 1.29e-13
                                      
(Intercept)                        ***
age_groupteen                         
age_groupadult                     ***
scale(Trial_number)                   
age_groupteen:scale(Trial_number)  ***
age_groupadult:scale(Trial_number) ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
             (Intr) ag_grpt ag_grpd sc(T_) ag_grpt:(T_)
age_grouptn  -0.611                                    
age_gropdlt  -0.710  0.433                             
scl(Trl_nm)  -0.001  0.001   0.001                     
ag_grpt:(T_)  0.000 -0.014   0.000  -0.611             
ag_grpd:(T_) -0.002  0.001   0.010  -0.680  0.417      

I tried to capture these effects in ‘individual difference’ variables by running the logistic regression separately for each subject in each condition. This wouldn’t capture anything different than the above analyses but I wanted to see if there were any subject-specific indices that could be correlated with other measues. I looked at three parameters:

  • The intercept: whether they are more or less likely to choose the optimal action having seen half of the trials (p>0.5 if intercept>0 (i.e. log(0.5/0.5)))
  • The slope: which direction and how fast the sigmoid moves in (for learning this must be positive and the larger it is the better the learning)
  • The learning index: where in the task (i.e. scaled trial number) they are at 50% for each machine (switch point - I came up with this to capture change in both parameters. I’m not sure if it makes sense.) The smaller the better (the sooner they learn the better choice).

Because each model is run only on 45 trials the fits aren’t great and the parameter distributions have large variances.

get_learning_coef <- function(data){
  model = glm(correct1_incorrect0 ~ scale(Trial_number), family = binomial(link=logit), data = data)
  b0 = coef(model)[1]
  b1 = coef(model)[2]
  learnIndex = -b0/b1                   
  return(data.frame(b0, b1, learnIndex))
}


tmp = machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  do(get_learning_coef(.)) %>%
  do(assign.age.info(.)) 

(Error bars not shown because they are very large due to bad fits).
As expected the difference between kids and adults in slopes for the high variance negative EV machine is visible here too.

tmp %>%
  ungroup()%>%
  select(facet_labels, age_group, b0, b1, learnIndex) %>%
  gather(key, value, -facet_labels, -age_group) %>%
  group_by(age_group, facet_labels, key) %>%
  summarise(mv = median(value),
            sv = sem(value)) %>%
  ggplot(aes(facet_labels, mv, fill=age_group))+
  geom_bar(stat="identity", position = position_dodge())+
  # geom_errorbar(aes(ymin = mv-sv, ymax = mv+sv), position = position_dodge(width = 0.9), width=0)+
  facet_wrap(~key, scale="free")+
  theme(legend.position = "bottom",
        legend.title = element_blank())+
  xlab("")+
  ylab("Median value")

But it’s not a good idea to look for group differences in these parameters as they are highly variable due to bad fits from few trials.

Correlation with BART

Quick look at how this relates to BART data:

adjusted.pumps <- function(subject_data){
  subject_data_adjusted = subject_data[subject_data$exploded == 0,]
  subject_pumps <- subject_data_adjusted %>% 
    group_by(trial.num) %>%
    summarise(total_pumps = sum(finished))
  out <- data.frame(mean_adjusted_pumps = mean(subject_pumps$total_pumps))
  return(out)
}

Increase in number of pumps with age

bart_data %>%
  group_by(Sub_id) %>%
  do(adjusted.pumps(.)) %>%
  do(assign.age.info(.)) %>%
  ggplot(aes(x=calc_age, y = mean_adjusted_pumps))+
  geom_point()+
  theme_bw()+
  geom_smooth(method = "lm") +
  xlab("Age")+
  ylab("Risk taking (adjusted pumps)")

There aren’t any meaningful correlations between slopes and mean adjusted pumps. BUT neither of these seem like good individual difference measures.

tmp = bart_data %>%
  group_by(Sub_id) %>%
  do(adjusted.pumps(.)) %>%
  do(assign.age.info(.)) %>%
  select(Sub_id, mean_adjusted_pumps)

machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  do(get_learning_coef(.)) %>%
  do(assign.age.info(.)) %>%
  left_join(tmp, by = 'Sub_id') %>%
  group_by(facet_labels, age_group) %>%
  summarise(cor = cor.test(b1, mean_adjusted_pumps)$estimate,
            p_value = cor.test(b1, mean_adjusted_pumps)$p.value) %>%
  arrange(cor)

Variance vs. EV sensitivity

Does it makes sense to look at these separately?

Since the machines differ in the variance of the outcomes and expected values it might seem sensible to look at which of these attributes has a larger effect on performance.

It’s tempting to tease apart the relative importance of these attributes for the high variance negative EV machine where we observe the performance difference between age groups.

BUT these attributes are correlated. So we can’t look at their effects separately in the same model.

#Function to calculate observed variance and observed expected value based on outcomes in trials that the subject has played.
get_obs_var_ev <- function(data){
  
  new_data = data
  new_data$obs_var <- NA
  new_data$obs_ev <-  NA
  
  for(i in 1:nrow(new_data)){
    if(i == 1){
      obs = 0
      obs_ev = 0
      obs_var = 0
    }
    else{
      #get all the trials until the current trial
      obs = new_data[1:i,]
      #filter only played trials; their belief should not be updated based on the trials they haven't played
      obs = obs %>% filter(Response == "play") %>% ungroup() %>% select(Points_earned)
      obs_var = var(obs)
      obs_probs =  as.numeric(prop.table(table(obs)))
      obs_rewards = as.numeric(names(prop.table(table(obs))))
      obs_ev = sum(obs_probs*obs_rewards)
    }
    new_data$obs_var[i] = obs_var
    new_data$obs_ev[i] = obs_ev
  }
  new_data$obs_var = ifelse(is.na(new_data$obs_var), 0, new_data$obs_var)
  return(new_data)
}
tmp = machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  do(get_obs_var_ev(.))
tmp %>%
  ggplot(aes(obs_var, obs_ev))+
  geom_point()+
  facet_wrap(~facet_labels, scales="free")+
  xlab("Observed variance")+
  ylab("Observed EV")

What we are interested in is the effect of beliefs about the machines on behavior. These beliefs can be summarized quantitatively in an ‘expected value.’

The cognitive processes that can differ with respect to this expected value can be how quickly it approaches the true expected value of a machine (the rate at which one incorporates each new data point to existing beliefs) and how truthfully the expected values are evaluated (is the utility of the expected value the same as its value).

These two processes can be captured as the learning rate and the exponent on the prediction error in an RL model.

Before moving on to modeling results here I plot the effect of observed EV (not model based) on choice to confirm that it makes sense and captures the behavioral effect:
The higher the EV of a machine the more likely it is to be played. This is the correct action for the positive EV machines but incorrect action for the negative EV machines. The behavioral effect in the high var negative EV machine is captured again with the diverging lines for age groups at low EVs.

tmp %>%
  ggplot(aes(obs_ev, correct1_incorrect0))+
  geom_line(aes(group = Sub_id, col= age_group),stat='smooth', method = 'glm', method.args = list(family = "binomial"), se = FALSE, alpha=0.2)+
  geom_line(aes(col= age_group),stat='smooth', method = 'glm', method.args = list(family = "binomial"), se = FALSE, alpha=1, size=2)+
  facet_wrap(~facet_labels, scales='free')+
  xlab("EV of played trials")+
  scale_y_continuous(breaks=c(0,1))+
  labs(col="Age group")+
  ylab('Correct')+
  theme(legend.position = "bottom",
        legend.title = element_blank())

Additional behavioral patterns

Though I focus on learning behavior and specifically difference in learning for the high variance negative EV machine there are other possible behavioral patterns that might also differ between the age groups. Here I list some examples.

Initial exploration

Do people ‘explore’ the first five trials where the reward probabilities for each machine are presented?

They explore less when they encounter a loss early on. In the high var pos EV machine they get 4 (small) losses in a row; in the low var negative EV machine they get a moderate loss in the first trial.

machine_game_data_clean %>% 
  group_by(Sub_id, facet_labels) %>%
  slice(1:5) %>%
  summarise(num_explored = sum(ifelse(Response == "play", 1,0))) %>%
  do(assign.age.info(.)) %>%
  ungroup() %>%
  group_by(age_group, facet_labels) %>%
  summarise(mean_num_explored = mean(num_explored/5*100),
            sem_num_explored = sem(num_explored/5*100)) %>%
  ggplot(aes(facet_labels, mean_num_explored, fill = age_group))+
  geom_bar(stat="identity",position = position_dodge(0.9))+
  geom_errorbar(aes(ymax = mean_num_explored+sem_num_explored, ymin = mean_num_explored-sem_num_explored), position = position_dodge(width = 0.9), width=0.25)+
  theme(legend.title = element_blank())+
  ylab("Percentage of exploration")+
  xlab("")

Memory effect

How does performance change depending on the delay between the last time a machine was played?

Can we think of this as a ‘memory effect’? The more trials since the last time you have played a machine, the more forgetting/interference?

For positive EV machines this is true for all groups. This is evident in the decreasing probability of a correct response the longer it has been since the last time a machine was played.

For negative EV machines adults and teens continue to make correct choices even if a lot of trials have passed since they last played that machine. Kids don’t seem to remember that the machine is ‘bad’ and are more likely to make an incorrect choice (and play the machine) the longer it’s been since they last played it.

machine_game_data_clean %>%
  group_by(Sub_id) %>%
  mutate(played_trial_number = ifelse(Response == "play", Trial_number, NA)) %>%
  mutate(played_trial_number = na.locf(played_trial_number, na.rm=F)) %>%
  filter(Trial_number > 1) %>%
  mutate(trials_since_last_played = Trial_number - lag(played_trial_number)) %>%
  group_by(trials_since_last_played,facet_labels, age_group) %>%
  summarise(mean_correct = mean(correct1_incorrect0, na.rm = T),
            sem_correct = sem(correct1_incorrect0)) %>%
  drop_na() %>%
  ggplot(aes(trials_since_last_played, mean_correct, col = age_group))+
  # geom_smooth(alpha=0.5, method='lm')+
  # geom_point()+
  geom_line(stat='smooth', method = 'glm', method.args = list(family = "binomial"), alpha=1, size=2)+
  facet_wrap(~facet_labels)+
  theme(legend.title = element_blank())+
  xlab("Trials since last played")+
  ylab("Mean Correct")

Post-loss behavior

If subjects are sensitive to losses and learning something about the machines in a way that overweights their most recent experience with the machine one sanity check is to compare how many trials it takes subjects to play a machine again after a loss versus a gain. Presumably the former would be higher than the latter. One might hesitate to play a machine again after a loss but be more likely to play it after a gain.

count.postoutcome.trials <- function(subject_data){
  
  loss_trials = which(subject_data$Points_earned<0)
  
  gain_trials = which(subject_data$Points_earned>0)
  
  play_trials= which(subject_data$Response == "play")
  
  post_loss_trials = play_trials[which(play_trials %in% loss_trials)+1]
  
  post_gain_trials = play_trials[which(play_trials %in% gain_trials)+1]
  
  num_trials_post_loss = post_loss_trials - loss_trials
  
  num_trials_post_gain = post_gain_trials - gain_trials
  
  if(length(num_trials_post_gain)>length(num_trials_post_loss)){
    num_trials_post_loss <- c(num_trials_post_loss, rep(NA, length(num_trials_post_gain) - length(num_trials_post_loss)))
  }
  else if(length(num_trials_post_gain)<length(num_trials_post_loss)){
    num_trials_post_gain <- c(num_trials_post_gain, rep(NA, length(num_trials_post_loss) - length(num_trials_post_gain)))
  }
  
  return(data.frame(num_trials_post_loss = num_trials_post_loss, num_trials_post_gain = num_trials_post_gain))
}

The plot below shows the average number of trials it takes a subject to play a given machine after experiencing a loss or a gain.

For everyone and for every machine the average number of trials it takes a subject to play following a loss is higher than the average number of trials it take them to play following a gain. This suggests that subjects are responding to outcomes in a way overweights their most recent experience with the machine.

tmp = machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  do(count.postoutcome.trials(.))  %>%
  do(assign.age.info(.)) %>%
  ungroup() %>%
  select(facet_labels, age_group, num_trials_post_loss, num_trials_post_gain, Sub_id) %>%
  gather(key, value, -facet_labels, -age_group, -Sub_id) %>%
  mutate(key = gsub("num_trials_post_", "", key)) 

tmp %>%
  group_by(facet_labels, age_group, key) %>%
  summarise(mean_post = mean(value, na.rm=T),
            sem_post = sem(value)) %>%
  ggplot(aes(age_group, mean_post, shape=key, col=age_group))+
  geom_point(size=2)+
  geom_errorbar(aes(ymin = mean_post-sem_post, ymax = mean_post+sem_post), width=0, size=2)+
  facet_wrap(~facet_labels)+
  xlab("Number of trials until next play")+
  theme(legend.title = element_blank())+
  guides(color=FALSE)

Reflecting the global behavior in proportion of playing in each condition adults take longer to play after large losses in the high variance negative EV condition compared to kids while kids are less sensitive to the magnitude of loss.

summary(lm(value~age_group*facet_labels,tmp %>%filter(key=="loss")))

Call:
lm(formula = value ~ age_group * facet_labels, data = tmp %>% 
    filter(key == "loss"))

Residuals:
    Min      1Q  Median      3Q     Max 
-1.8711 -0.4617 -0.4138 -0.2342 26.8007 

Coefficients:
                                    Estimate Std. Error t value Pr(>|t|)
(Intercept)                          1.41929    0.07378  19.236  < 2e-16
age_groupteen                       -0.18511    0.11688  -1.584 0.113334
age_groupadult                      -0.15072    0.10193  -1.479 0.139327
facet_labels-5,+495                  0.25664    0.09722   2.640 0.008327
facet_labels+10,-100                 0.78004    0.11923   6.542 6.84e-11
facet_labels+5,-495                  0.56908    0.18878   3.015 0.002590
age_groupteen:facet_labels-5,+495   -0.02910    0.15335  -0.190 0.849487
age_groupadult:facet_labels-5,+495  -0.11137    0.13387  -0.832 0.405494
age_groupteen:facet_labels+10,-100   0.35057    0.19689   1.781 0.075069
age_groupadult:facet_labels+10,-100  0.82253    0.18041   4.559 5.29e-06
age_groupteen:facet_labels+5,-495    0.40507    0.31298   1.294 0.195654
age_groupadult:facet_labels+5,-495   1.00609    0.28488   3.532 0.000418
                                       
(Intercept)                         ***
age_groupteen                          
age_groupadult                         
facet_labels-5,+495                 ** 
facet_labels+10,-100                ***
facet_labels+5,-495                 ** 
age_groupteen:facet_labels-5,+495      
age_groupadult:facet_labels-5,+495     
age_groupteen:facet_labels+10,-100  .  
age_groupadult:facet_labels+10,-100 ***
age_groupteen:facet_labels+5,-495      
age_groupadult:facet_labels+5,-495  ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.611 on 3982 degrees of freedom
  (1675 observations deleted due to missingness)
Multiple R-squared:  0.07091,   Adjusted R-squared:  0.06834 
F-statistic: 27.63 on 11 and 3982 DF,  p-value: < 2.2e-16

One thought that is not necessarily immediately pertinent but that I puzzled over is how this graph would have looked like if subjects were taking all their experiences with the machine in to account (instead of overweighing their most recent experience). I have a vague intuition that in that case the difference in responding between the experiences (gain/loss) would be 0. That is, if one takes in to account all their experiences then they would distinguish between the positive and negative EV machines and either always play for positive EV machines or never play for negative EV machines regardless of the observed outcome. Relatedly then, this difference in response patterns depending on the observed outcome could be due to at least two reasons: memory or loss aversion. Or perhaps stronger memories for losses. I’m not sure where I’m going with this but perhaps there is something interesting to look at in the hippocampal activity following losses versus gains.

Cross-talk between machines

Are subjects less likely to play overall after a loss or only less likely to play that machine after a loss for that machine?

mean.postloss.play.prob <- function(subject_data){
  
  Sub_id = unique(subject_data$Sub_id)
  
  loss_trials = which(subject_data$Points_earned<0)
  
  mean_post_loss_prob <- mean(ifelse(subject_data$Response[loss_trials+1] == "play", 1, 0), na.rm=T)
  
  return(data.frame(mean_post_loss_prob=mean_post_loss_prob))
}

Probability of playing following a loss depends on machine type. Looking at all trials masks this difference. Subjects seem to learn machine specifically and cross-talk isn’t evident here.

tmp = machine_game_data_clean %>%
  group_by(Sub_id) %>%
  do(mean.postloss.play.prob(.)) %>%
  mutate(facet_labels = "all_trials")

machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  do(mean.postloss.play.prob(.)) %>%
  rbind(tmp) %>%
  do(assign.age.info(.)) %>%
  group_by(age_group, facet_labels) %>%
  summarise(mp = mean(mean_post_loss_prob,na.rm=T),
            sp = sem(mean_post_loss_prob)) %>%
  ggplot(aes(facet_labels, mp, fill=age_group))+
  geom_bar(stat="identity",position=position_dodge())+
  geom_errorbar(width=0, aes(ymin = mp-sp, ymax = mp+sp), position = position_dodge(width=0.9))+
  xlab("")+
  ylab("Post loss play probability")+
  theme(legend.title = element_blank())

Response time differences

machine_game_data_clean %>%
  ggplot(aes(log(Reaction_time))) +
  geom_density(aes(fill = age_group), alpha=0.5, color=NA) +
  facet_wrap(~facet_labels)+
  theme(legend.title = element_blank())+
  ylab("")+
  xlab("Log Response Time")

machine_game_data_clean %>%
  group_by(Sub_id, facet_labels) %>%
  summarise(mean_log_rt = mean(log(Reaction_time)),
            sem_log_rt = sem(log(Reaction_time))) %>%
  do(assign.age.info(.)) %>%
  ggplot(aes(age_group, mean_log_rt))+
  geom_boxplot(aes(fill=age_group))+
  facet_wrap(~facet_labels)+
  theme(legend.position = "none")+
  ylab("Mean Log Rt")+
  xlab("Age group")

Both teens and adults are faster than kids in all conditions but the high var negative EV.

#summary(lmer(log(Reaction_time) ~ age_group*facet_labels +(1|Sub_id), data = machine_game_data_clean))

summary(lmer(log(Reaction_time) ~ age_group +(1|Sub_id), data = machine_game_data_clean%>%filter(facet_labels == "-10,+100")))
Linear mixed model fit by REML ['lmerMod']
Formula: log(Reaction_time) ~ age_group + (1 | Sub_id)
   Data: machine_game_data_clean %>% filter(facet_labels == "-10,+100")

REML criterion at convergence: 3528.4

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.5400 -0.6540 -0.1091  0.5703  3.8968 

Random effects:
 Groups   Name        Variance Std.Dev.
 Sub_id   (Intercept) 0.03464  0.1861  
 Residual             0.15763  0.3970  
Number of obs: 3371, groups:  Sub_id, 75

Fixed effects:
               Estimate Std. Error t value
(Intercept)     6.99491    0.03627 192.868
age_groupteen  -0.20684    0.05966  -3.467
age_groupadult -0.21703    0.05129  -4.231

Correlation of Fixed Effects:
            (Intr) ag_grpt
age_grouptn -0.608        
age_gropdlt -0.707  0.430 
summary(lmer(log(Reaction_time) ~ age_group +(1|Sub_id), data = machine_game_data_clean%>%filter(facet_labels == "-5,+495")))
Linear mixed model fit by REML ['lmerMod']
Formula: log(Reaction_time) ~ age_group + (1 | Sub_id)
   Data: machine_game_data_clean %>% filter(facet_labels == "-5,+495")

REML criterion at convergence: 4061.8

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.5760 -0.6509 -0.1139  0.6302  3.1327 

Random effects:
 Groups   Name        Variance Std.Dev.
 Sub_id   (Intercept) 0.04803  0.2192  
 Residual             0.18413  0.4291  
Number of obs: 3370, groups:  Sub_id, 75

Fixed effects:
               Estimate Std. Error t value
(Intercept)     6.99842    0.04239 165.082
age_groupteen  -0.14905    0.06974  -2.137
age_groupadult -0.11796    0.05996  -1.967

Correlation of Fixed Effects:
            (Intr) ag_grpt
age_grouptn -0.608        
age_gropdlt -0.707  0.430 
summary(lmer(log(Reaction_time) ~ age_group +(1|Sub_id), data = machine_game_data_clean%>%filter(facet_labels == "+10,-100")))
Linear mixed model fit by REML ['lmerMod']
Formula: log(Reaction_time) ~ age_group + (1 | Sub_id)
   Data: machine_game_data_clean %>% filter(facet_labels == "+10,-100")

REML criterion at convergence: 3801.4

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.2648 -0.6823 -0.1028  0.6509  3.1213 

Random effects:
 Groups   Name        Variance Std.Dev.
 Sub_id   (Intercept) 0.02927  0.1711  
 Residual             0.17155  0.4142  
Number of obs: 3375, groups:  Sub_id, 75

Fixed effects:
               Estimate Std. Error t value
(Intercept)     7.03189    0.03377 208.207
age_groupteen  -0.12876    0.05556  -2.318
age_groupadult -0.12068    0.04776  -2.527

Correlation of Fixed Effects:
            (Intr) ag_grpt
age_grouptn -0.608        
age_gropdlt -0.707  0.430 
summary(lmer(log(Reaction_time) ~ age_group +(1|Sub_id), data = machine_game_data_clean%>%filter(facet_labels == "+5,-495")))
Linear mixed model fit by REML ['lmerMod']
Formula: log(Reaction_time) ~ age_group + (1 | Sub_id)
   Data: machine_game_data_clean %>% filter(facet_labels == "+5,-495")

REML criterion at convergence: 3872.8

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.0286 -0.6820 -0.0941  0.6227  3.9061 

Random effects:
 Groups   Name        Variance Std.Dev.
 Sub_id   (Intercept) 0.03565  0.1888  
 Residual             0.17409  0.4172  
Number of obs: 3384, groups:  Sub_id, 75

Fixed effects:
               Estimate Std. Error t value
(Intercept)     6.98863    0.03691 189.320
age_groupteen  -0.08277    0.06072  -1.363
age_groupadult -0.08177    0.05220  -1.567

Correlation of Fixed Effects:
            (Intr) ag_grpt
age_grouptn -0.608        
age_gropdlt -0.707  0.430 

RL modeling

Details of model comparison can be found in a separate notebook.

Based on a comparison of 6 models the best fitting model is a 4 parameter model with one learning rate (\(\alpha\)) and two exponents (\(\gamma\)) weighing positive and negative prediction errors separately (along with the inverse softmax temperature \(\beta\) ).

In line with the above analyses where the behavioral difference lay in the high variance negative EV machine the age difference in the parameters is found in:

  1. exponent over negative prediction errors: Adults distort negative outcomes less. A negative outcome doesn’t feel as bad as it is for kids

  2. inverse softmax temperature: Adults make decisions based more on EV compared to kids

Therefore the trial level PEs from this model will be used as a parametric regressor for the imaging analyses.

LS0tCnRpdGxlOiAiRGV2ZWxvcG1lbnRhbCBkaWZmZXJlbmNlcyBpbiBzZW5zaXRpdml0eSB0byBoaWdoIHZhcmlhbmNlIGZlZWRiYWNrIgpvdXRwdXQ6IApodG1sX2RvY3VtZW50Ogp0b2M6IHRydWUKdG9jX2RlcHRzOiAyCi0tLQoKYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc291cmNlKCcvVXNlcnMvemV5bmVwZW5rYXZpL0Ryb3Bib3gvUG9sZHJhY2tMYWIvRGV2U3R1ZHlfQW5hbHlzZXMvY29kZS93b3Jrc3BhY2Vfc2NyaXB0cy9EZXZTdHVkeV93b3Jrc3BhY2UuUicpCmBgYAoKVGhpcyBub3RlYm9vayBjb250YWlucyBleHBsb3JhdG9yeSBhbmFseXNlcyBvZiBiZWhhdmlvcmFsIGRhdGEgY29sbGVjdGVkIHRvIGludmVzdGlnYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiByaXNrIHRha2luZyBiZWhhdmlvciBhbmQgcHJvYmFiaWxpc3RpYyBsZWFybmluZy4gIAoKVGhlIHNhbXBsZSBjb25zaXN0cyBvZiB0aHJlZSBhZ2UgZ3JvdXBzOiBraWRzLCB0ZWVucyBhbmQgYWR1bHRzIGFuZCB3ZSBoeXBvdGhlc2l6ZSB0aGF0IHNlbnNpdGl2aXR5IHRvIGxlYXJuIGZyb20gaGlnaCB2YXJpYW5jZSBmZWVkYmFjayBpbXByb3ZlcyB3aXRoIGFnZSAoYW5kIHRoaXMgaXMgcmVsYXRlZCB0byBiZXR0ZXIgcmlza3kgZGVjaXNpb25zKS4gIAoKU3ViamVjdHMgY29tcGxldGVkIGEgcHJvYmFiaWxpc3RpYyBsZWFybmluZyB0YXNrIGluIHRoZSBzY2FubmVyLCBhIHJpc2t5IGRlY2lzaW9uIG1ha2luZyB0YXNrIChCQVJUKSBvdXRzaWRlIHRoZSBzY2FubmVyIGFuZCBudW1lcm91cyBxdWVzdGlvbm5haXJlcy4gVGhlIGZvY3VzIG9mIHRoaXMgbm90ZWJvb2sgaXMgb24gdGhlIGZpcnN0IHRhc2suICAKClRoZSBwbGFuIG9mIGFuYWx5c2lzIGlzIHRvIGVzdGFibGlzaCB0aGF0IGFkdWx0cyBhcmUgbW9yZSBzZW5zaXRpdmUgdG8gaGlnaCB2YXJpYW5jZSBmZWVkYmFjayBpbiB0aGUgcHJvYmFiaWxpc3RpYyBsZWFybmluZyB0YXNrIGFuZCByZWxhdGUgdGhpcyAobW9kZWxlZCkgc2Vuc2l0aXZpdHkgdG8gYmVoYXZpb3IgaW4gQkFSVC4gIAoKIyBTYW1wbGUgaW5mbwoKRmlyc3QgbGV0J3MgZ2V0IGEgc2Vuc2Ugb2YgdGhlIHNhbXBsZS4gSGVyZSBpcyBob3cgbWFueSBzdWJqZWN0cyB3ZSBoYXZlIHdobyBoYXZlIGNvbXBsZXRlIGRhdGFzZXRzIGZvciB0aGUgcHJvYmFiaWxpc3RpYyBsZWFybmluZyB0YXNrIGFuZCB0aGVpciBhZ2UgYnJlYWsgZG93bnMuCgpgYGB7ciBzYW1wbGVfaW5mbywgd2FybmluZz1GQUxTRX0KbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lIAogIGdyb3VwX2J5KGFnZV9ncm91cCkgJT4lCiAgc3VtbWFyaXNlKG1pbl9hZ2UgPSBtaW4oY2FsY19hZ2UpLAogICAgICAgICAgICBtZWFuX2FnZSA9IG1lYW4oY2FsY19hZ2UpLAogICAgICAgICAgICBzZF9hZ2UgPSBzZChjYWxjX2FnZSksCiAgICAgICAgICAgIG1heF9hZ2UgPSBtYXgoY2FsY19hZ2UpLAogICAgICAgICAgICBuID0gbigpLzE4MCkKYGBgCgojIFBlcmZvcm1hbmNlIGluIFJMIHRhc2sKCkluIHRoaXMgdGFzayBzdWJqZWN0cyBhcmUgcHJlc2VudGVkIHdpdGggYSBmcmFjdGFsIGluIGVhY2ggdHJpYWwuIFRoZSBmcmFjdGFscyByZXByZXNlbnQgZGlmZmVyZW50IG1hY2hpbmVzIChzaW5nbGUtYXJtZWQgYmFuZGl0cykuIFN1YmplY3RzIGNob29zZSB0byBwbGF5IG9yIHBhc3MgaW4gZWFjaCB0cmlhbC4gRWFjaCBtYWNoaW5lIHlpZWxkcyBhIHByb2JhYmlsaXN0aWMgcmV3YXJkLiBUaGVyZSBhcmUgZm91ciBtYWNoaW5lcyBpbiB0b3RhbC4gVHdvIHdpdGggcG9zaXRpdmUgYW5kIHR3byB3aXRoIG5lZ2F0aXZlIGV4cGVjdGVkIHZhbHVlLiBPbmUgb2YgZWFjaCBvZiB0aGVzZSBtYWNoaW5lcyBoYXMgYSBsb3cgdmFyaWFuY2UgcmV3YXJkIHNjaGVkdWxlIHdoaWxlIHRoZSBvdGhlciBoYXMgYSBoaWdoIHZhcmlhbmNlIHJld2FyZCBzY2hlZHVsZS4gCgotIE9uZSBtYWNoaW5lIGdpdmVzIFwkNSA5MCUgb2YgdGhlIHRpbWUgYW5kIC1cJDQ5NSAlMTAgb2YgdGhlIHRpbWUgIAotIE9uZSBtYWNoaW5lIGdpdmVzIC1cJDUgOTAlIG9mIHRoZSB0aW1lIGFuZCBcJDQ5NSAlMTAgb2YgdGhlIHRpbWUgIAotIE9uZSBtYWNoaW5lIGdpdmVzIFwkMTAgNTAlIG9mIHRoZSB0aW1lIGFuZCAtJDEwMCAlNTAgb2YgdGhlIHRpbWUgIAotIE9uZSBtYWNoaW5lIGdpdmVzIC1cJDEwIDUwJSBvZiB0aGUgdGltZSBhbmQgJDEwMCAlNTAgb2YgdGhlIHRpbWUgIAoKIyMgUG9pbnRzIGVhcm5lZAoKUGVyZm9ybWFuY2UgaW4gdGhpcyB0YXNrIGNhbiBiZSBhc3Nlc3NlZCBieSBsb29raW5nIGF0IHRoZSB0b3RhbCBudW1iZXIgb2YgcG9pbnRzIHN1YmplY3RzIG1ha2UgYXQgdGhlIGVuZCBvZiB0YXNrLiBUaGUgZm9sbG93aW5nIGdyYXBoIHNob3dzIHRoYXQgYWR1bHRzIGNvbGxlY3QgbW9yZSBwb2ludHMgaW4gdGhpcyB0YXNrIGNvbXBhcmVkIHRvIGtpZHMuCgpgYGB7cn0KbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9wb2ludHMgPSBzdW0oUG9pbnRzX2Vhcm5lZCkpICU+JQogIGRvKGFzc2lnbi5hZ2UuaW5mbyguKSkgJT4lCiAgZ3JvdXBfYnkoYWdlX2dyb3VwKSAlPiUKICBzdW1tYXJpc2UobWVhbl9wb2ludHMgPSBtZWFuKHRvdGFsX3BvaW50cyksCiAgICAgICAgICAgIHNlbV9wb2ludHMgPSBzZW0odG90YWxfcG9pbnRzKSkgJT4lCiAgZ2dwbG90KGFlcyhhZ2VfZ3JvdXAsIG1lYW5fcG9pbnRzKSkrCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCgwLjkpKSkrCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuX3BvaW50cy1zZW1fcG9pbnRzLCB5bWF4PW1lYW5fcG9pbnRzK3NlbV9wb2ludHMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoPTAuMjUpKwogIHRoZW1lX2J3KCkrCiAgeGxhYignTWFjaGluZScpKwogIHlsYWIoJ01lYW4gcG9pbnRzJykrCiAgbGFicyhmaWxsPSdBZ2UgZ3JvdXAnKQoKYGBgCgpUaGlzIGRpZmZlcmVuY2UgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudDogYWR1bHRzIGVhcm4gbW9yZSBwb2ludHMgY29tcGFyZWQgdG8gdGhlIGtpZHMuCgpgYGB7cn0KdG1wID0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkKSAlPiUKICBzdW1tYXJpc2UodG90YWxfcG9pbnRzID0gc3VtKFBvaW50c19lYXJuZWQpKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpCgpzdW1tYXJ5KGxtKHRvdGFsX3BvaW50c35hZ2VfZ3JvdXAsIGRhdGE9dG1wKSkKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnJtKHRtcCkKYGBgCgpTaW5jZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgYWdlIGRpZmZlcmVuY2VzIGJldHdlZW4gc2Vuc2l0aXZpdHkgdG8gZGlmZmVyZW50IGZlZWRiYWNrIHNjaGVkdWxlcywgKip3ZSBzaG91bGQgc2hvdyB0aGF0IHRoaXMgZGlmZmVyZW5jZSBpbiBwZXJmb3JtYW5jZSBleGlzdHMgZXNwZWNpYWxseSBmb3IgdGhlIGhpZ2ggdmFyaWFuY2UgZmVlZGJhY2sgY29uZGl0aW9uKHMpKiouIEhlcmUgaXMgdGhlIHBsb3Qgb2YgcGVyZm9ybWFuY2UgKHRvdGFsIHBvaW50cyBlYXJuZWQpIGJyb2tlbiBkb3duIGJ5IGNvbmRpdGlvbnMuCgpgYGB7cn0KbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9wb2ludHMgPSBzdW0oUG9pbnRzX2Vhcm5lZCkpICU+JQogIGRvKGFzc2lnbi5hZ2UuaW5mbyguKSkgJT4lCiAgZ3JvdXBfYnkoYWdlX2dyb3VwLCBmYWNldF9sYWJlbHMpICU+JQogIHN1bW1hcmlzZShtZWFuX3BvaW50cyA9IG1lYW4odG90YWxfcG9pbnRzKSwKICAgICAgICAgICAgc2VtX3BvaW50cyA9IHNlbSh0b3RhbF9wb2ludHMpKSAlPiUKICBnZ3Bsb3QoYWVzKGZhY2V0X2xhYmVscywgbWVhbl9wb2ludHMsIGZpbGw9YWdlX2dyb3VwKSkrCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCgwLjkpKSkrCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuX3BvaW50cy1zZW1fcG9pbnRzLCB5bWF4PW1lYW5fcG9pbnRzK3NlbV9wb2ludHMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoPTAuMjUpKwogICMgdGhlbWVfYncoKSsKICB4bGFiKCdNYWNoaW5lJykrCiAgeWxhYignTWVhbiBwb2ludHMnKSsKICBsYWJzKGZpbGw9J0FnZSBncm91cCcpCgpnZ3NhdmUoIlBvaW50c19lYXJuZWQuanBlZyIsIGRldmljZSA9ICJqcGVnIiwgcGF0aCA9IGZpZ19wYXRoLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwgZHBpID0gNDUwKQpgYGAKClJ1bm5pbmcgc2VwYXJhdGUgbW9kZWxzIGZvciBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgRVYgbWFjaGluZXMgZm9yIGVhc2Ugb2YgaW50ZXJwcmV0YXRpb24uCgpgYGB7cn0KdG1wIDwtIG1hY2hpbmVfZ2FtZV9kYXRhX2NsZWFuICU+JQogIGdyb3VwX2J5KFN1Yl9pZCwgZmFjZXRfbGFiZWxzKSAlPiUKICBzdW1tYXJpc2UodG90YWxfcG9pbnRzID0gc3VtKFBvaW50c19lYXJuZWQpKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpCmBgYAoKSW4gdGhlIHBvc2l0aXZlIEVWIG1hY2hpbmVzIHRoZXJlIGlzIGEgbWFpbiBlZmZlY3QgZm9yIHRoZSBoaWdoIHZhcmlhbmNlIG1hY2hpbmUuIFN1YmplY3RzIGVhcm4gZmV3ZXIgcG9pbnRzIGluIHRoZSBoaWdoIHZhcmlhbmNlIGNvbmRpdGlvbiBjb21wYXJlZCB0byB0aGUgbG93IHZhcmlhbmNlIGNvbmRpdGlvbi4gVGhlcmUgYXJlIG5vIGFnZSBkaWZmZXJlbmNlcy4KCmBgYHtyfQpzdW1tYXJ5KGxtKHRvdGFsX3BvaW50cyB+IGFnZV9ncm91cCpmYWNldF9sYWJlbHMsIGRhdGEgPSB0bXAgJT4lIGZpbHRlcihmYWNldF9sYWJlbHMgJWluJSBjKCItMTAsKzEwMCIsICItNSwrNDk1IikpKSkKYGBgCgpJbiB0aGUgbmVnYXRpdmUgRVYgbWFjaGluZXMgdGhlcmUgaXMgYWdhaW4gYSBtYWluIGVmZmVjdCBmb3IgdGhlIGhpZ2ggdmFyaWFuY2UgbWFjaGluZTogRXZlcnlvbmUgbG9zc2VzIGZld2VyIHBvaW50cyBpbiB0aGUgbG93IHZhcmlhbmNlIGNvbmRpdGlvbi4gVGhlcmUgaXMgYWxzbyBhIG1haW4gZWZmZWN0IGZvciBhZHVsdHM6IEFkdWx0cyBwZXJmb3JtIGJldHRlciB0aGFuIGtpZHMgZm9yIGJvdGggbmVnYXRpdmUgRVYgbWFjaGluZXMuCgpgYGB7cn0Kc3VtbWFyeShsbSh0b3RhbF9wb2ludHMgfiBhZ2VfZ3JvdXAqZmFjZXRfbGFiZWxzLCBkYXRhID0gdG1wICU+JSBmaWx0ZXIoZmFjZXRfbGFiZWxzICVpbiUgYygiKzEwLC0xMDAiLCAiKzUsLTQ5NSIpKSkpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0Kcm0odG1wKQpgYGAKCioqU28gdGhlIGFnZSBkaWZmZW5jZSBpbiBwZXJmb3JtYW5jZSBpcyBkcml2ZW4gYnkgZGlmZmVyZW5jZSBpbiBwZXJmb3JtYW5jZSBpbiBuZWdhdGl2ZSBFViBtYWNoaW5lcy4gVGhlIHF1ZXN0aW9uIGlzIHdoYXQgZGlmZmVyZW5jZSBpbiBiZWhhdmlvciBpbiB0aGVzZSBjb25kaXRpb25zIGlzIGxlYWRpbmcgdG8gdGhpcyBkaWZmZXJlbmNlIGluIHBlcmZvcm1hbmNlPyoqICAgIAoKVG8gYW50aWNpcGF0ZSBwb3NzaWJsZSBjb2duaXRpdmUgcHJvY2Vzc2VzIHRoYXQgd2lsbCBiZSBwYXJhbWV0ZXJpemVkIGluIFJMIG1vZGVscyBkaWZmZXJlbmNlcyBjYW4gbGllIGluOiBob3cgcXVpY2tseSB0aGUgZ3JvdXBzIGxlYXJuIHRoZSBwcm9iYWJpbGl0aWVzLCBob3cgbXVjaCB3ZWlnaHQgdGhleSBwdXQgb24gdGhlIG91dGNvbWVzIGFuZC9vciBob3cgbXVjaCBsaWtlIGFuIG9wdGltYWwgYWdlbnQgdGhleSBiZWhhdmUuCgojIyBQcm9wb3J0aW9uIG9mIHBsYXlpbmcKClRoZSBmaXJzdCB0aGluZyB3ZSBjYW4gbG9vayBhdCBpcyBob3cgb2Z0ZW4gc3ViamVjdHMgcGxheSB2ZXJzdXMgcGFzcy4gSXQncyBoYXJkIHRvIHNlZSBhbnkgYWdlIGRpZmZlcmVuY2VzIHdoZW4gd2UganVzdCBsb29rIGF0IGZyZXF1ZW5jeSBvZiBvdmVyYWxsIHBsYXlpbmcgYXMgYmVsb3cuICAgCgpgYGB7cn0KbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBSZXNwb25zZSkgJT4lCiAgdGFsbHkgJT4lCiAgZ3JvdXBfYnkoU3ViX2lkKSAlPiUKICBtdXRhdGUocGN0PSgxMDAqbikvc3VtKG4pKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpICU+JQogIGdyb3VwX2J5KGFnZV9ncm91cCwgUmVzcG9uc2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UobWVhbl9wY3QgPSBtZWFuKHBjdCksCiAgICAgICAgICAgIHNlbV9wY3QgPSBzZW0ocGN0KSkgJT4lCiAgZ2dwbG90KGFlcyhSZXNwb25zZSwgbWVhbl9wY3QsIGZpbGwgPSBhZ2VfZ3JvdXApKSsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC45KSkrCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW5fcGN0IC0gc2VtX3BjdCwgeW1heCA9IG1lYW5fcGN0ICsgc2VtX3BjdCksIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCB3aWR0aD0wLjI1KSsKICB0aGVtZV9idygpKwogIHlsYWIoJ1BlcmNlbnRhZ2Ugb2YgdHJpYWxzJykrCiAgbGFicyhmaWxsID0gJ0FnZSBncm91cCcpCmBgYAoKSXQgaXMgYWxzbyBub3QgaW1tZWRpYXRlbHkgYXBwYXJlbnQgaG93IHRvIHRyYW5zbGF0ZSB0aGlzIHRvIGJldHRlciBwZXJmb3JtYW5jZS9sZWFybmluZyBpbiB0aGlzIHRhc2sgYnV0IG9uZSB3YXkgdG8gdGhpbmsgYWJvdXQgaXQ6IElmIHBlb3BsZSBsZWFybmVkIHBlcmZlY3RseSB0aGV5IHNob3VsZCBwbGF5IGhhbGYgb2YgdGhlIHRpbWUgKGFsd2F5cyBmb3IgdGhlIHBvc2l0aXZlIGV4cGVjdGVkIHZhbHVlIHRyaWFsIGFuZCBuZXZlciBmb3IgdGhlIG5lZ2F0aXZlIGV4cGVjdGVkIHZhbHVlIHRyaWFscykuIFRoZSBmYWN0IHRoYXQgYWxsIHBsYXkgcHJvcG9ydGlvbnMgYXJlIGFib3ZlIDUwJSBzdWdnZXN0cyB0aGF0IG5vYm9keSBsZWFybnMgcGVyZmVjdGx5IGFuZCB0aGF0IGFkdWx0cyBtaWdodCBiZSBjbG9zZXN0IHRvIGl0LiBCdXQgdGhpcyBpcyB2ZXJ5IGNydWRlIGFuZCBhIGJldHRlciB3YXkgdG8gbG9vayBhdCBpdCB3b3VsZCBiZSB0byBzZWUgICAKCjEuIGhvdyB0aGlzIGRlcGVuZHMgb24gdGhlIGRpZmZlcmVudCBtYWNoaW5lcyBhbmQgICAKMi4gaG93IGl0IGNoYW5nZXMgdGhyb3VnaG91dCB0aGUgdGFzay4KClRvIGdldCBhIGJldHRlciBzZW5zZSBvZiBvdmVyYWxsIGJlaGF2aW9yIGluIGRpZmZlcmVudCBjb250aW5nZW5jeSBzdGF0ZXMgd2UgYnJlYWsgdGhpcyBwcm9wb3J0aW9uIG9mIHBsYXlpbmcgZG93biBieSBtYWNoaW5lcy4KCk5vdyB3ZSBjYW4gc2VlIGFnZSBkaWZmZXJlbmNlcyBpbiBwbGF5aW5nIGZyZXF1ZW5jeSBpbiBkaWZmZXJlbnQgY29uZGl0aW9ucywgcGFydGljdWxhcmx5IGluIHRoZSBuZWdhdGl2ZSBleHBlY3RlZCB2YWx1ZSBtYWNoaW5lcyAoYm90dG9tIHJvdykuCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiAlPiUKICBncm91cF9ieShTdWJfaWQsIGZhY2V0X2xhYmVscywgUmVzcG9uc2UpICU+JQogIHRhbGx5ICU+JQogIGdyb3VwX2J5KFN1Yl9pZCwgZmFjZXRfbGFiZWxzKSAlPiUKICBtdXRhdGUocGN0PSgxMDAqbikvc3VtKG4pKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpICU+JQogIGdyb3VwX2J5KGFnZV9ncm91cCwgZmFjZXRfbGFiZWxzLCBSZXNwb25zZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShtZWFuX3BjdCA9IG1lYW4ocGN0KSwKICAgICAgICAgICAgc2VtX3BjdCA9IHNlbShwY3QpKSAlPiUKICBnZ3Bsb3QoYWVzKFJlc3BvbnNlLCBtZWFuX3BjdCwgZmlsbCA9IGFnZV9ncm91cCkpKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpKSsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWVhbl9wY3QgLSBzZW1fcGN0LCB5bWF4ID0gbWVhbl9wY3QgKyBzZW1fcGN0KSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoPTAuMjUpKwogIHRoZW1lX2J3KCkrCiAgeWxhYignUGVyY2VudGFnZSBvZiB0cmlhbHMnKSsKICBmYWNldF93cmFwKH5mYWNldF9sYWJlbHMpKwogIGxhYnMoZmlsbCA9ICdBZ2UgZ3JvdXAnKQpgYGAKClRoZSBkaWZmZXJlbmNlcyBpbiBwb2ludHMgZWFybmVkIG1hcCBkaXJlY3RseSBvbiB0byBwcm9wb3J0aW9uIG9mIGNob29zaW5nIHRvIHBsYXkgZWFjaCBtYWNoaW5lOiAgCgotIEFkdWx0cyBwbGF5IGxlc3MgdGhhbiBraWRzIGZvciBib3RoIG5lZ2F0aXZlIEVWIG1hY2hpbmVzLiAKLSBFdmVyeW9uZSBwbGF5cyB0aGUgaGlnaCB2YXIgcG9zaXRpdmUgRVYgbWFjaGluZSBsZXNzIHRoYW4gdGhlIGxvdyB2YXIgcG9zaXRpdmUgRVYgbWFjaGluZS4KLSBFdmVyeW9uZSBwbGF5cyB0aGUgbG93IHZhciBuZWdhdGl2ZSBFViBtYWNoaW5lcyBsZXNzIHRoYW4gdGhlIGxvdyB2YXIgcG9zaXRpdmUgRVYgbWFjaGluZS4KCmBgYHtyfQp0bXAgPC0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMsIFJlc3BvbnNlKSAlPiUKICB0YWxseSAlPiUKICBncm91cF9ieShTdWJfaWQsIGZhY2V0X2xhYmVscykgJT4lCiAgbXV0YXRlKHBjdF9wbGF5PSgxMDAqbikvc3VtKG4pKSAlPiUKICBmaWx0ZXIoUmVzcG9uc2UgPT0gJ3BsYXknKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpCgpzdW1tYXJ5KGxtZXIocGN0X3BsYXkgfiBhZ2VfZ3JvdXAqZmFjZXRfbGFiZWxzICsgKDF8U3ViX2lkKSwgZGF0YSA9IHRtcCkpCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpybSh0bXApCmBgYAoKVGhpcyBpcyBub3Qgc3VycHJpc2luZyBnaXZlbiB3aGF0IHRoZSBudW1iZXIgb2YgcG9pbnRzIGVhcm5lZCBhbHJlYWR5IHNob3dlZC4gQnV0IG5vdyB0aGF0IHdlIGFyZSBsb29raW5nIGF0IGEgYmVoYXZpb3JhbCBtZWFzdXJlIGluc3RlYWQgb2YgYW4gb3V0Y29tZSBtZWFzdXJlIHdlIG1pZ2h0IGJlIGFibGUgdG8gcXVhbnRpZnkgY29uc3RydWN0cyBvZiBpbnRlcmVzdCBsaWtlIHNlbnNpdGl2aXR5IHRvIHZhcmlhbmNlIG9yIHNlbnNpdGl2aXR5IHRvIHRoZSBleHBlY3RlZCB2YWx1ZXMgb2YgdGhlIG1hY2hpbmVzLiAgCgpBcyBhIGZpcnN0IHN0ZXAgdG8gdHJhbnNsYXRlIHJhdyBwbGF5aW5nIGJlaGF2aW9yIHRvIGxlYXJuaW5nIEkgcmVjb2RlZCB0aGUgY2hvaWNlcyB0byBiZSBgY29ycmVjdGAgd2hlbiBhIHN1YmplY3QgY2hvb3NlcyB0byBwbGF5IGEgcG9zaXRpdmUgZXhwZWN0ZWQgdmFsdWUgbWFjaGluZSBhbmQgcGFzcyBhIG5lZ2F0aXZlIGV4cGVjdGVkIHZhbHVlIG1hY2hpbmUgYW5kIGBpbmNvcnJlY3RgIHdoZW4gdGhlIHJldmVyc2UgaXMgdHJ1ZS4gSWYgYSBzdWJqZWN0IGlzIGxlYXJuaW5nIHRoZXkgc2hvdWxkIGJlIGxlYXJuaW5nIHRvIHBsYXkgdGhlIHBvc2l0aXZlIGV4cGVjdGVkIG1hY2hpbmVzIGFuZCB0byBwYXNzIHRoZSBvdGhlcnMuCgojIyBMZWFybmluZyAKClJlY29kaW5nIHRoZSBiZWhhdmlvciBpbiB0aGlzIHdheSBnYXZlIGEgY2xlYXJlciBwaWN0dXJlIG9mIHRoZSBhZ2UgZGlmZmVyZW5jZSAqaW4gbGVhcm5pbmcgb2Ygb3B0aW1hbCBiZWhhdmlvciogYmV0d2VlbiB0aGUgY29uZGl0aW9ucy4gU3BlY2lmaWNhbGx5IHdlIGNhbiBub3cgbG9vayBhdCBob3cgdGhlIHByb2JhYmlsaXR5IG9mIGEgY29ycmVjdCBjaG9pY2UgY2hhbmdlcyBmb3IgZWFjaCBhZ2UgZ3JvdXAgaW4gZWFjaCBjb25kaXRpb24gYWNyb3NzIHRyaWFscy4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm1hY2hpbmVfZ2FtZV9kYXRhX2NsZWFuICU+JQogIGdncGxvdChhZXMoc2NhbGUoVHJpYWxfbnVtYmVyKSwgY29ycmVjdDFfaW5jb3JyZWN0MCkpKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBTdWJfaWQsIGNvbD0gZmFjdG9yKGFnZV9ncm91cCwgbGV2ZWxzPWMoJ2tpZCcsICd0ZWVuJywgJ2FkdWx0JykpKSxzdGF0PSdzbW9vdGgnLCBtZXRob2QgPSAnZ2xtJywgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCBzZSA9IEZBTFNFLCBhbHBoYT0wLjIpKwogIGdlb21fbGluZShhZXMoY29sPSBmYWN0b3IoYWdlX2dyb3VwLCBsZXZlbHM9Yygna2lkJywgJ3RlZW4nLCAnYWR1bHQnKSkpLHN0YXQ9J3Ntb290aCcsIG1ldGhvZCA9ICdnbG0nLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIHNlID0gRkFMU0UsIGFscGhhPTEsIHNpemU9MikrCiAgZmFjZXRfd3JhcCh+ZmFjZXRfbGFiZWxzKSsKICB0aGVtZV9idygpKwogIHhsYWIoIlJlbGF0aXZlIHRyaWFsIG51bWJlciIpKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9YygwLDEpKSsKICBsYWJzKGNvbD0iQWdlIGdyb3VwIikrCiAgeWxhYignQ29ycmVjdCBjaG9pY2UnKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQoKZ2dzYXZlKCJMZWFybmluZy5qcGVnIiwgZGV2aWNlID0gImpwZWciLCBwYXRoID0gZmlnX3BhdGgsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iLCBkcGkgPSA0NTApCmBgYAoKTG9va2luZyBhdCBsZWFybmluZyBlZmZlY3RzIHNlcGFyYXRlbHkgZm9yIGVhY2ggbWFjaGluZSB0byBhdm9pZCBpbnRlcnByZXRpbmcgbWVzc3kgdGhyZWUtd2F5IGludGVyYWN0aW9ucy4KCkFkdWx0cyBhcmUgbW9yZSBsaWtlbHkgdG8gbWFrZSBjb3JyZWN0IGRlY2lzaW9ucyBpbiBsb3cgdmFyIHBvc2l0aXZlIEVWIG1hY2hpbmUuCgpgYGB7cn0Kc3VtbWFyeShnbG1lcihjb3JyZWN0MV9pbmNvcnJlY3QwIH4gYWdlX2dyb3VwKnNjYWxlKFRyaWFsX251bWJlcikrKDF8U3ViX2lkKSwgZGF0YSA9IG1hY2hpbmVfZ2FtZV9kYXRhX2NsZWFuICU+JSBmaWx0ZXIoZmFjZXRfbGFiZWxzICVpbiUgYygnLTEwLCsxMDAnKSksIGZhbWlseT1iaW5vbWlhbCkpCmBgYAoKVGhlIHByb2JhYmlsaXR5IG9mIG1ha2luZyBhIGNvcnJlY3QgcmVzcG9uc2UgZm9yIHRoZSBoaWdoIHZhciBwb3NpdGl2ZSBFViBtYWNoaW5lIGRvZXNuJ3QgY2hhbmdlIGZvciBhZHVsdHMgb3Iga2lkcyBidXQgaW5jcmVhc2VzIGZvciB0ZWVucyBhY3Jvc3MgdHJpYWxzLgoKYGBge3J9CnN1bW1hcnkoZ2xtZXIoY29ycmVjdDFfaW5jb3JyZWN0MCB+IGFnZV9ncm91cCpzY2FsZShUcmlhbF9udW1iZXIpKygxfFN1Yl9pZCksIGRhdGEgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiAlPiUgZmlsdGVyKGZhY2V0X2xhYmVscyAlaW4lIGMoJy01LCs0OTUnKSksIGZhbWlseT1iaW5vbWlhbCkpCmBgYAoKQWxsIGdyb3VwcyBzaG93IGltcHJvdmVtZW50IGFjcm9zcyB0cmlhbHMgZm9yIHRoZSBsb3cgdmFyIG5lZ2F0aXZlIEVWIG1hY2hpbmUgYnV0IGFkdWx0cyBsZWFybiBmYXN0ZXIgdGhhbiBraWRzIGFuZCB0ZWVucy4KCmBgYHtyfQpzdW1tYXJ5KGdsbWVyKGNvcnJlY3QxX2luY29ycmVjdDAgfiBhZ2VfZ3JvdXAqc2NhbGUoVHJpYWxfbnVtYmVyKSsoMXxTdWJfaWQpLCBkYXRhID0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lIGZpbHRlcihmYWNldF9sYWJlbHMgJWluJSBjKCcrMTAsLTEwMCcpKSwgZmFtaWx5PWJpbm9taWFsKSkKYGBgCgpLaWRzIGRvbid0IHNob3cgbGVhcm5pbmcgYWNyb3NzIHRyaWFscyBmb3IgdGhlIGhpZ2ggdmFyIG5lZ2F0aXZlIEVWIG1hY2hpbmUgYnV0IGFkdWx0cyBhbmQgdGVlbnMgZG8uIAoKYGBge3J9CnN1bW1hcnkoZ2xtZXIoY29ycmVjdDFfaW5jb3JyZWN0MCB+IGFnZV9ncm91cCpzY2FsZShUcmlhbF9udW1iZXIpKygxfFN1Yl9pZCksIGRhdGEgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiU+JSBmaWx0ZXIoZmFjZXRfbGFiZWxzICVpbiUgYygnKzUsLTQ5NScpKSwgZmFtaWx5PWJpbm9taWFsKSkKYGBgCgpJIHRyaWVkIHRvIGNhcHR1cmUgdGhlc2UgZWZmZWN0cyBpbiAnaW5kaXZpZHVhbCBkaWZmZXJlbmNlJyB2YXJpYWJsZXMgYnkgcnVubmluZyB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBzZXBhcmF0ZWx5IGZvciBlYWNoIHN1YmplY3QgaW4gZWFjaCBjb25kaXRpb24uIFRoaXMgd291bGRuJ3QgY2FwdHVyZSBhbnl0aGluZyBkaWZmZXJlbnQgdGhhbiB0aGUgYWJvdmUgYW5hbHlzZXMgYnV0IEkgd2FudGVkIHRvIHNlZSBpZiB0aGVyZSB3ZXJlIGFueSBzdWJqZWN0LXNwZWNpZmljIGluZGljZXMgdGhhdCBjb3VsZCBiZSBjb3JyZWxhdGVkIHdpdGggb3RoZXIgbWVhc3Vlcy4gSSBsb29rZWQgYXQgdGhyZWUgcGFyYW1ldGVyczoKCi0gVGhlIGludGVyY2VwdDogd2hldGhlciB0aGV5IGFyZSBtb3JlIG9yIGxlc3MgbGlrZWx5IHRvIGNob29zZSB0aGUgb3B0aW1hbCBhY3Rpb24gaGF2aW5nIHNlZW4gaGFsZiBvZiB0aGUgdHJpYWxzIChwPjAuNSBpZiBpbnRlcmNlcHQ+MCAoaS5lLiBsb2coMC41LzAuNSkpKQotIFRoZSBzbG9wZTogd2hpY2ggZGlyZWN0aW9uIGFuZCBob3cgZmFzdCB0aGUgc2lnbW9pZCBtb3ZlcyBpbiAoZm9yIGxlYXJuaW5nIHRoaXMgbXVzdCBiZSBwb3NpdGl2ZSBhbmQgdGhlIGxhcmdlciBpdCBpcyB0aGUgYmV0dGVyIHRoZSBsZWFybmluZykgIAotIFRoZSBsZWFybmluZyBpbmRleDogd2hlcmUgaW4gdGhlIHRhc2sgKGkuZS4gc2NhbGVkIHRyaWFsIG51bWJlcikgdGhleSBhcmUgYXQgNTAlIGZvciBlYWNoIG1hY2hpbmUgKHN3aXRjaCBwb2ludCAtIEkgY2FtZSB1cCB3aXRoIHRoaXMgdG8gY2FwdHVyZSBjaGFuZ2UgaW4gYm90aCBwYXJhbWV0ZXJzLiBJJ20gbm90IHN1cmUgaWYgaXQgbWFrZXMgc2Vuc2UuKSBUaGUgc21hbGxlciB0aGUgYmV0dGVyICh0aGUgc29vbmVyIHRoZXkgbGVhcm4gdGhlIGJldHRlciBjaG9pY2UpLiAgCgpCZWNhdXNlIGVhY2ggbW9kZWwgaXMgcnVuIG9ubHkgb24gNDUgdHJpYWxzIHRoZSBmaXRzIGFyZW4ndCBncmVhdCBhbmQgdGhlIHBhcmFtZXRlciBkaXN0cmlidXRpb25zIGhhdmUgbGFyZ2UgdmFyaWFuY2VzLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZXRfbGVhcm5pbmdfY29lZiA8LSBmdW5jdGlvbihkYXRhKXsKICBtb2RlbCA9IGdsbShjb3JyZWN0MV9pbmNvcnJlY3QwIH4gc2NhbGUoVHJpYWxfbnVtYmVyKSwgZmFtaWx5ID0gYmlub21pYWwobGluaz1sb2dpdCksIGRhdGEgPSBkYXRhKQogIGIwID0gY29lZihtb2RlbClbMV0KICBiMSA9IGNvZWYobW9kZWwpWzJdCiAgbGVhcm5JbmRleCA9IC1iMC9iMSAgICAgICAgICAgICAgICAgICAKICByZXR1cm4oZGF0YS5mcmFtZShiMCwgYjEsIGxlYXJuSW5kZXgpKQp9CgoKdG1wID0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMpICU+JQogIGRvKGdldF9sZWFybmluZ19jb2VmKC4pKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpIApgYGAKCihFcnJvciBiYXJzIG5vdCBzaG93biBiZWNhdXNlIHRoZXkgYXJlIHZlcnkgbGFyZ2UgZHVlIHRvIGJhZCBmaXRzKS4gIApBcyBleHBlY3RlZCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGtpZHMgYW5kIGFkdWx0cyBpbiBzbG9wZXMgZm9yIHRoZSBoaWdoIHZhcmlhbmNlIG5lZ2F0aXZlIEVWIG1hY2hpbmUgaXMgdmlzaWJsZSBoZXJlIHRvby4KCmBgYHtyfQp0bXAgJT4lCiAgdW5ncm91cCgpJT4lCiAgc2VsZWN0KGZhY2V0X2xhYmVscywgYWdlX2dyb3VwLCBiMCwgYjEsIGxlYXJuSW5kZXgpICU+JQogIGdhdGhlcihrZXksIHZhbHVlLCAtZmFjZXRfbGFiZWxzLCAtYWdlX2dyb3VwKSAlPiUKICBncm91cF9ieShhZ2VfZ3JvdXAsIGZhY2V0X2xhYmVscywga2V5KSAlPiUKICBzdW1tYXJpc2UobXYgPSBtZWRpYW4odmFsdWUpLAogICAgICAgICAgICBzdiA9IHNlbSh2YWx1ZSkpICU+JQogIGdncGxvdChhZXMoZmFjZXRfbGFiZWxzLCBtdiwgZmlsbD1hZ2VfZ3JvdXApKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkrCiAgIyBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbXYtc3YsIHltYXggPSBtditzdiksIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCB3aWR0aD0wKSsKICBmYWNldF93cmFwKH5rZXksIHNjYWxlPSJmcmVlIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCIiKSsKICB5bGFiKCJNZWRpYW4gdmFsdWUiKQpgYGAKCioqQnV0IGl0J3Mgbm90IGEgZ29vZCBpZGVhIHRvIGxvb2sgZm9yIGdyb3VwIGRpZmZlcmVuY2VzIGluIHRoZXNlIHBhcmFtZXRlcnMgYXMgdGhleSBhcmUgaGlnaGx5IHZhcmlhYmxlIGR1ZSB0byBiYWQgZml0cyBmcm9tIGZldyB0cmlhbHMuKioKCiMjIENvcnJlbGF0aW9uIHdpdGggQkFSVAoKUXVpY2sgbG9vayBhdCBob3cgdGhpcyByZWxhdGVzIHRvIEJBUlQgZGF0YToKCmBgYHtyfQphZGp1c3RlZC5wdW1wcyA8LSBmdW5jdGlvbihzdWJqZWN0X2RhdGEpewogIHN1YmplY3RfZGF0YV9hZGp1c3RlZCA9IHN1YmplY3RfZGF0YVtzdWJqZWN0X2RhdGEkZXhwbG9kZWQgPT0gMCxdCiAgc3ViamVjdF9wdW1wcyA8LSBzdWJqZWN0X2RhdGFfYWRqdXN0ZWQgJT4lIAogICAgZ3JvdXBfYnkodHJpYWwubnVtKSAlPiUKICAgIHN1bW1hcmlzZSh0b3RhbF9wdW1wcyA9IHN1bShmaW5pc2hlZCkpCiAgb3V0IDwtIGRhdGEuZnJhbWUobWVhbl9hZGp1c3RlZF9wdW1wcyA9IG1lYW4oc3ViamVjdF9wdW1wcyR0b3RhbF9wdW1wcykpCiAgcmV0dXJuKG91dCkKfQpgYGAKCkluY3JlYXNlIGluIG51bWJlciBvZiBwdW1wcyB3aXRoIGFnZQoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KYmFydF9kYXRhICU+JQogIGdyb3VwX2J5KFN1Yl9pZCkgJT4lCiAgZG8oYWRqdXN0ZWQucHVtcHMoLikpICU+JQogIGRvKGFzc2lnbi5hZ2UuaW5mbyguKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWNhbGNfYWdlLCB5ID0gbWVhbl9hZGp1c3RlZF9wdW1wcykpKwogIGdlb21fcG9pbnQoKSsKICB0aGVtZV9idygpKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICB4bGFiKCJBZ2UiKSsKICB5bGFiKCJSaXNrIHRha2luZyAoYWRqdXN0ZWQgcHVtcHMpIikKYGBgCgoqKlRoZXJlIGFyZW4ndCBhbnkgbWVhbmluZ2Z1bCBjb3JyZWxhdGlvbnMgYmV0d2VlbiBzbG9wZXMgYW5kIG1lYW4gYWRqdXN0ZWQgcHVtcHMuICpCVVQqIG5laXRoZXIgb2YgdGhlc2Ugc2VlbSBsaWtlIGdvb2QgaW5kaXZpZHVhbCBkaWZmZXJlbmNlIG1lYXN1cmVzLioqCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQp0bXAgPSBiYXJ0X2RhdGEgJT4lCiAgZ3JvdXBfYnkoU3ViX2lkKSAlPiUKICBkbyhhZGp1c3RlZC5wdW1wcyguKSkgJT4lCiAgZG8oYXNzaWduLmFnZS5pbmZvKC4pKSAlPiUKICBzZWxlY3QoU3ViX2lkLCBtZWFuX2FkanVzdGVkX3B1bXBzKQoKbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMpICU+JQogIGRvKGdldF9sZWFybmluZ19jb2VmKC4pKSAlPiUKICBkbyhhc3NpZ24uYWdlLmluZm8oLikpICU+JQogIGxlZnRfam9pbih0bXAsIGJ5ID0gJ1N1Yl9pZCcpICU+JQogIGdyb3VwX2J5KGZhY2V0X2xhYmVscywgYWdlX2dyb3VwKSAlPiUKICBzdW1tYXJpc2UoY29yID0gY29yLnRlc3QoYjEsIG1lYW5fYWRqdXN0ZWRfcHVtcHMpJGVzdGltYXRlLAogICAgICAgICAgICBwX3ZhbHVlID0gY29yLnRlc3QoYjEsIG1lYW5fYWRqdXN0ZWRfcHVtcHMpJHAudmFsdWUpICU+JQogIGFycmFuZ2UoY29yKQpgYGAKYGBge3IgZWNobz1GQUxTRX0Kcm0odG1wKQpgYGAKCiMjIFZhcmlhbmNlIHZzLiBFViBzZW5zaXRpdml0eQoKRG9lcyBpdCBtYWtlcyBzZW5zZSB0byBsb29rIGF0IHRoZXNlIHNlcGFyYXRlbHk/ICAKClNpbmNlIHRoZSBtYWNoaW5lcyBkaWZmZXIgaW4gdGhlIHZhcmlhbmNlIG9mIHRoZSBvdXRjb21lcyBhbmQgZXhwZWN0ZWQgdmFsdWVzIGl0IG1pZ2h0IHNlZW0gc2Vuc2libGUgdG8gbG9vayBhdCB3aGljaCBvZiB0aGVzZSBhdHRyaWJ1dGVzIGhhcyBhIGxhcmdlciBlZmZlY3Qgb24gcGVyZm9ybWFuY2UuICAKCkl0J3MgdGVtcHRpbmcgdG8gdGVhc2UgYXBhcnQgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgdGhlc2UgYXR0cmlidXRlcyBmb3IgdGhlIGhpZ2ggdmFyaWFuY2UgbmVnYXRpdmUgRVYgbWFjaGluZSB3aGVyZSB3ZSBvYnNlcnZlIHRoZSBwZXJmb3JtYW5jZSBkaWZmZXJlbmNlIGJldHdlZW4gYWdlIGdyb3Vwcy4gIAoKKipCVVQgdGhlc2UgYXR0cmlidXRlcyBhcmUgY29ycmVsYXRlZC4gU28gd2UgY2FuJ3QgbG9vayBhdCB0aGVpciBlZmZlY3RzIHNlcGFyYXRlbHkgaW4gdGhlIHNhbWUgbW9kZWwuKiogICAKCmBgYHtyfQojRnVuY3Rpb24gdG8gY2FsY3VsYXRlIG9ic2VydmVkIHZhcmlhbmNlIGFuZCBvYnNlcnZlZCBleHBlY3RlZCB2YWx1ZSBiYXNlZCBvbiBvdXRjb21lcyBpbiB0cmlhbHMgdGhhdCB0aGUgc3ViamVjdCBoYXMgcGxheWVkLgpnZXRfb2JzX3Zhcl9ldiA8LSBmdW5jdGlvbihkYXRhKXsKICAKICBuZXdfZGF0YSA9IGRhdGEKICBuZXdfZGF0YSRvYnNfdmFyIDwtIE5BCiAgbmV3X2RhdGEkb2JzX2V2IDwtICBOQQogIAogIGZvcihpIGluIDE6bnJvdyhuZXdfZGF0YSkpewogICAgaWYoaSA9PSAxKXsKICAgICAgb2JzID0gMAogICAgICBvYnNfZXYgPSAwCiAgICAgIG9ic192YXIgPSAwCiAgICB9CiAgICBlbHNlewogICAgICAjZ2V0IGFsbCB0aGUgdHJpYWxzIHVudGlsIHRoZSBjdXJyZW50IHRyaWFsCiAgICAgIG9icyA9IG5ld19kYXRhWzE6aSxdCiAgICAgICNmaWx0ZXIgb25seSBwbGF5ZWQgdHJpYWxzOyB0aGVpciBiZWxpZWYgc2hvdWxkIG5vdCBiZSB1cGRhdGVkIGJhc2VkIG9uIHRoZSB0cmlhbHMgdGhleSBoYXZlbid0IHBsYXllZAogICAgICBvYnMgPSBvYnMgJT4lIGZpbHRlcihSZXNwb25zZSA9PSAicGxheSIpICU+JSB1bmdyb3VwKCkgJT4lIHNlbGVjdChQb2ludHNfZWFybmVkKQogICAgICBvYnNfdmFyID0gdmFyKG9icykKICAgICAgb2JzX3Byb2JzID0gIGFzLm51bWVyaWMocHJvcC50YWJsZSh0YWJsZShvYnMpKSkKICAgICAgb2JzX3Jld2FyZHMgPSBhcy5udW1lcmljKG5hbWVzKHByb3AudGFibGUodGFibGUob2JzKSkpKQogICAgICBvYnNfZXYgPSBzdW0ob2JzX3Byb2JzKm9ic19yZXdhcmRzKQogICAgfQogICAgbmV3X2RhdGEkb2JzX3ZhcltpXSA9IG9ic192YXIKICAgIG5ld19kYXRhJG9ic19ldltpXSA9IG9ic19ldgogIH0KICBuZXdfZGF0YSRvYnNfdmFyID0gaWZlbHNlKGlzLm5hKG5ld19kYXRhJG9ic192YXIpLCAwLCBuZXdfZGF0YSRvYnNfdmFyKQogIHJldHVybihuZXdfZGF0YSkKfQpgYGAKCmBgYHtyfQp0bXAgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiAlPiUKICBncm91cF9ieShTdWJfaWQsIGZhY2V0X2xhYmVscykgJT4lCiAgZG8oZ2V0X29ic192YXJfZXYoLikpCmBgYAoKYGBge3J9CnRtcCAlPiUKICBnZ3Bsb3QoYWVzKG9ic192YXIsIG9ic19ldikpKwogIGdlb21fcG9pbnQoKSsKICBmYWNldF93cmFwKH5mYWNldF9sYWJlbHMsIHNjYWxlcz0iZnJlZSIpKwogIHhsYWIoIk9ic2VydmVkIHZhcmlhbmNlIikrCiAgeWxhYigiT2JzZXJ2ZWQgRVYiKQpgYGAKCldoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW4gaXMgdGhlIGVmZmVjdCBvZiBiZWxpZWZzIGFib3V0IHRoZSBtYWNoaW5lcyBvbiBiZWhhdmlvci4gVGhlc2UgYmVsaWVmcyBjYW4gYmUgc3VtbWFyaXplZCBxdWFudGl0YXRpdmVseSBpbiBhbiAnZXhwZWN0ZWQgdmFsdWUuJyAgCgpUaGUgY29nbml0aXZlIHByb2Nlc3NlcyB0aGF0IGNhbiBkaWZmZXIgd2l0aCByZXNwZWN0IHRvIHRoaXMgZXhwZWN0ZWQgdmFsdWUgY2FuIGJlIGhvdyBxdWlja2x5IGl0IGFwcHJvYWNoZXMgdGhlIHRydWUgZXhwZWN0ZWQgdmFsdWUgb2YgYSBtYWNoaW5lICh0aGUgcmF0ZSBhdCB3aGljaCBvbmUgaW5jb3Jwb3JhdGVzIGVhY2ggbmV3IGRhdGEgcG9pbnQgdG8gZXhpc3RpbmcgYmVsaWVmcykgYW5kIGhvdyB0cnV0aGZ1bGx5IHRoZSBleHBlY3RlZCB2YWx1ZXMgYXJlIGV2YWx1YXRlZCAoaXMgdGhlIHV0aWxpdHkgb2YgdGhlIGV4cGVjdGVkIHZhbHVlIHRoZSBzYW1lIGFzIGl0cyB2YWx1ZSkuICAgCgpUaGVzZSB0d28gcHJvY2Vzc2VzIGNhbiBiZSBjYXB0dXJlZCBhcyB0aGUgbGVhcm5pbmcgcmF0ZSBhbmQgdGhlIGV4cG9uZW50IG9uIHRoZSBwcmVkaWN0aW9uIGVycm9yIGluIGFuIFJMIG1vZGVsLgoKQmVmb3JlIG1vdmluZyBvbiB0byBtb2RlbGluZyByZXN1bHRzIGhlcmUgSSBwbG90IHRoZSBlZmZlY3Qgb2Ygb2JzZXJ2ZWQgRVYgKG5vdCBtb2RlbCBiYXNlZCkgb24gY2hvaWNlIHRvIGNvbmZpcm0gdGhhdCBpdCBtYWtlcyBzZW5zZSBhbmQgY2FwdHVyZXMgdGhlIGJlaGF2aW9yYWwgZWZmZWN0OiAgClRoZSBoaWdoZXIgdGhlIEVWIG9mIGEgbWFjaGluZSB0aGUgbW9yZSBsaWtlbHkgaXQgaXMgdG8gYmUgcGxheWVkLiBUaGlzIGlzIHRoZSBjb3JyZWN0IGFjdGlvbiBmb3IgdGhlIHBvc2l0aXZlIEVWIG1hY2hpbmVzIGJ1dCBpbmNvcnJlY3QgYWN0aW9uIGZvciB0aGUgbmVnYXRpdmUgRVYgbWFjaGluZXMuIFRoZSBiZWhhdmlvcmFsIGVmZmVjdCBpbiB0aGUgaGlnaCB2YXIgbmVnYXRpdmUgRVYgbWFjaGluZSBpcyBjYXB0dXJlZCBhZ2FpbiB3aXRoIHRoZSBkaXZlcmdpbmcgbGluZXMgZm9yIGFnZSBncm91cHMgYXQgbG93IEVWcy4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnRtcCAlPiUKICBnZ3Bsb3QoYWVzKG9ic19ldiwgY29ycmVjdDFfaW5jb3JyZWN0MCkpKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBTdWJfaWQsIGNvbD0gYWdlX2dyb3VwKSxzdGF0PSdzbW9vdGgnLCBtZXRob2QgPSAnZ2xtJywgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCBzZSA9IEZBTFNFLCBhbHBoYT0wLjIpKwogIGdlb21fbGluZShhZXMoY29sPSBhZ2VfZ3JvdXApLHN0YXQ9J3Ntb290aCcsIG1ldGhvZCA9ICdnbG0nLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIHNlID0gRkFMU0UsIGFscGhhPTEsIHNpemU9MikrCiAgZmFjZXRfd3JhcCh+ZmFjZXRfbGFiZWxzLCBzY2FsZXM9J2ZyZWUnKSsKICB4bGFiKCJFViBvZiBwbGF5ZWQgdHJpYWxzIikrCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1jKDAsMSkpKwogIGxhYnMoY29sPSJBZ2UgZ3JvdXAiKSsKICB5bGFiKCdDb3JyZWN0JykrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIEFkZGl0aW9uYWwgYmVoYXZpb3JhbCBwYXR0ZXJucwoKVGhvdWdoIEkgZm9jdXMgb24gbGVhcm5pbmcgYmVoYXZpb3IgYW5kIHNwZWNpZmljYWxseSBkaWZmZXJlbmNlIGluIGxlYXJuaW5nIGZvciB0aGUgaGlnaCB2YXJpYW5jZSBuZWdhdGl2ZSBFViBtYWNoaW5lIHRoZXJlIGFyZSBvdGhlciBwb3NzaWJsZSBiZWhhdmlvcmFsIHBhdHRlcm5zIHRoYXQgbWlnaHQgYWxzbyBkaWZmZXIgYmV0d2VlbiB0aGUgYWdlIGdyb3Vwcy4gSGVyZSBJIGxpc3Qgc29tZSBleGFtcGxlcy4KCiMjIyBJbml0aWFsIGV4cGxvcmF0aW9uCgpEbyBwZW9wbGUgJ2V4cGxvcmUnIHRoZSBmaXJzdCBmaXZlIHRyaWFscyB3aGVyZSB0aGUgcmV3YXJkIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggbWFjaGluZSBhcmUgcHJlc2VudGVkPwoKVGhleSBleHBsb3JlIGxlc3Mgd2hlbiB0aGV5IGVuY291bnRlciBhIGxvc3MgZWFybHkgb24uIEluIHRoZSBoaWdoIHZhciBwb3MgRVYgbWFjaGluZSB0aGV5IGdldCA0IChzbWFsbCkgbG9zc2VzIGluIGEgcm93OyBpbiB0aGUgbG93IHZhciBuZWdhdGl2ZSBFViBtYWNoaW5lIHRoZXkgZ2V0IGEgbW9kZXJhdGUgbG9zcyBpbiB0aGUgZmlyc3QgdHJpYWwuCgpgYGB7cn0KbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lIAogIGdyb3VwX2J5KFN1Yl9pZCwgZmFjZXRfbGFiZWxzKSAlPiUKICBzbGljZSgxOjUpICU+JQogIHN1bW1hcmlzZShudW1fZXhwbG9yZWQgPSBzdW0oaWZlbHNlKFJlc3BvbnNlID09ICJwbGF5IiwgMSwwKSkpICU+JQogIGRvKGFzc2lnbi5hZ2UuaW5mbyguKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KGFnZV9ncm91cCwgZmFjZXRfbGFiZWxzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9udW1fZXhwbG9yZWQgPSBtZWFuKG51bV9leHBsb3JlZC81KjEwMCksCiAgICAgICAgICAgIHNlbV9udW1fZXhwbG9yZWQgPSBzZW0obnVtX2V4cGxvcmVkLzUqMTAwKSkgJT4lCiAgZ2dwbG90KGFlcyhmYWNldF9sYWJlbHMsIG1lYW5fbnVtX2V4cGxvcmVkLCBmaWxsID0gYWdlX2dyb3VwKSkrCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC45KSkrCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1heCA9IG1lYW5fbnVtX2V4cGxvcmVkK3NlbV9udW1fZXhwbG9yZWQsIHltaW4gPSBtZWFuX251bV9leHBsb3JlZC1zZW1fbnVtX2V4cGxvcmVkKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoPTAuMjUpKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkrCiAgeWxhYigiUGVyY2VudGFnZSBvZiBleHBsb3JhdGlvbiIpKwogIHhsYWIoIiIpCmBgYAoKIyMjIE1lbW9yeSBlZmZlY3QKCkhvdyBkb2VzIHBlcmZvcm1hbmNlIGNoYW5nZSBkZXBlbmRpbmcgb24gdGhlIGRlbGF5IGJldHdlZW4gdGhlIGxhc3QgdGltZSBhIG1hY2hpbmUgd2FzIHBsYXllZD8gIAoKQ2FuIHdlIHRoaW5rIG9mIHRoaXMgYXMgYSAnbWVtb3J5IGVmZmVjdCc/IFRoZSBtb3JlIHRyaWFscyBzaW5jZSB0aGUgbGFzdCB0aW1lIHlvdSBoYXZlIHBsYXllZCBhIG1hY2hpbmUsIHRoZSBtb3JlIGZvcmdldHRpbmcvaW50ZXJmZXJlbmNlPyAKCkZvciBwb3NpdGl2ZSBFViBtYWNoaW5lcyB0aGlzIGlzIHRydWUgZm9yIGFsbCBncm91cHMuIFRoaXMgaXMgZXZpZGVudCBpbiB0aGUgZGVjcmVhc2luZyBwcm9iYWJpbGl0eSBvZiBhIGNvcnJlY3QgcmVzcG9uc2UgdGhlIGxvbmdlciBpdCBoYXMgYmVlbiBzaW5jZSB0aGUgbGFzdCB0aW1lIGEgbWFjaGluZSB3YXMgcGxheWVkLgoKRm9yIG5lZ2F0aXZlIEVWIG1hY2hpbmVzIGFkdWx0cyBhbmQgdGVlbnMgY29udGludWUgdG8gbWFrZSBjb3JyZWN0IGNob2ljZXMgZXZlbiBpZiBhIGxvdCBvZiB0cmlhbHMgaGF2ZSBwYXNzZWQgc2luY2UgdGhleSBsYXN0IHBsYXllZCB0aGF0IG1hY2hpbmUuIEtpZHMgZG9uJ3Qgc2VlbSB0byByZW1lbWJlciB0aGF0IHRoZSBtYWNoaW5lIGlzICdiYWQnIGFuZCBhcmUgbW9yZSBsaWtlbHkgdG8gbWFrZSBhbiBpbmNvcnJlY3QgY2hvaWNlIChhbmQgcGxheSB0aGUgbWFjaGluZSkgdGhlIGxvbmdlciBpdCdzIGJlZW4gc2luY2UgdGhleSBsYXN0IHBsYXllZCBpdC4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm1hY2hpbmVfZ2FtZV9kYXRhX2NsZWFuICU+JQogIGdyb3VwX2J5KFN1Yl9pZCkgJT4lCiAgbXV0YXRlKHBsYXllZF90cmlhbF9udW1iZXIgPSBpZmVsc2UoUmVzcG9uc2UgPT0gInBsYXkiLCBUcmlhbF9udW1iZXIsIE5BKSkgJT4lCiAgbXV0YXRlKHBsYXllZF90cmlhbF9udW1iZXIgPSBuYS5sb2NmKHBsYXllZF90cmlhbF9udW1iZXIsIG5hLnJtPUYpKSAlPiUKICBmaWx0ZXIoVHJpYWxfbnVtYmVyID4gMSkgJT4lCiAgbXV0YXRlKHRyaWFsc19zaW5jZV9sYXN0X3BsYXllZCA9IFRyaWFsX251bWJlciAtIGxhZyhwbGF5ZWRfdHJpYWxfbnVtYmVyKSkgJT4lCiAgZ3JvdXBfYnkodHJpYWxzX3NpbmNlX2xhc3RfcGxheWVkLGZhY2V0X2xhYmVscywgYWdlX2dyb3VwKSAlPiUKICBzdW1tYXJpc2UobWVhbl9jb3JyZWN0ID0gbWVhbihjb3JyZWN0MV9pbmNvcnJlY3QwLCBuYS5ybSA9IFQpLAogICAgICAgICAgICBzZW1fY29ycmVjdCA9IHNlbShjb3JyZWN0MV9pbmNvcnJlY3QwKSkgJT4lCiAgZHJvcF9uYSgpICU+JQogIGdncGxvdChhZXModHJpYWxzX3NpbmNlX2xhc3RfcGxheWVkLCBtZWFuX2NvcnJlY3QsIGNvbCA9IGFnZV9ncm91cCkpKwogICMgZ2VvbV9zbW9vdGgoYWxwaGE9MC41LCBtZXRob2Q9J2xtJykrCiAgIyBnZW9tX3BvaW50KCkrCiAgZ2VvbV9saW5lKHN0YXQ9J3Ntb290aCcsIG1ldGhvZCA9ICdnbG0nLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIGFscGhhPTEsIHNpemU9MikrCiAgZmFjZXRfd3JhcCh+ZmFjZXRfbGFiZWxzKSsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpKwogIHhsYWIoIlRyaWFscyBzaW5jZSBsYXN0IHBsYXllZCIpKwogIHlsYWIoIk1lYW4gQ29ycmVjdCIpCiAgCmBgYAoKIyMjIFBvc3QtbG9zcyBiZWhhdmlvcgoKSWYgc3ViamVjdHMgYXJlIHNlbnNpdGl2ZSB0byBsb3NzZXMgYW5kIGxlYXJuaW5nIHNvbWV0aGluZyBhYm91dCB0aGUgbWFjaGluZXMgaW4gYSB3YXkgdGhhdCBvdmVyd2VpZ2h0cyB0aGVpciBtb3N0IHJlY2VudCBleHBlcmllbmNlIHdpdGggdGhlIG1hY2hpbmUgb25lIHNhbml0eSBjaGVjayBpcyB0byBjb21wYXJlIGhvdyBtYW55IHRyaWFscyBpdCB0YWtlcyBzdWJqZWN0cyB0byBwbGF5IGEgbWFjaGluZSBhZ2FpbiBhZnRlciBhIGxvc3MgdmVyc3VzIGEgZ2Fpbi4gUHJlc3VtYWJseSB0aGUgZm9ybWVyIHdvdWxkIGJlIGhpZ2hlciB0aGFuIHRoZSBsYXR0ZXIuIE9uZSBtaWdodCBoZXNpdGF0ZSB0byBwbGF5IGEgbWFjaGluZSBhZ2FpbiBhZnRlciBhIGxvc3MgYnV0IGJlIG1vcmUgbGlrZWx5IHRvIHBsYXkgaXQgYWZ0ZXIgYSBnYWluLgoKYGBge3J9CmNvdW50LnBvc3RvdXRjb21lLnRyaWFscyA8LSBmdW5jdGlvbihzdWJqZWN0X2RhdGEpewogIAogIGxvc3NfdHJpYWxzID0gd2hpY2goc3ViamVjdF9kYXRhJFBvaW50c19lYXJuZWQ8MCkKICAKICBnYWluX3RyaWFscyA9IHdoaWNoKHN1YmplY3RfZGF0YSRQb2ludHNfZWFybmVkPjApCiAgCiAgcGxheV90cmlhbHM9IHdoaWNoKHN1YmplY3RfZGF0YSRSZXNwb25zZSA9PSAicGxheSIpCiAgCiAgcG9zdF9sb3NzX3RyaWFscyA9IHBsYXlfdHJpYWxzW3doaWNoKHBsYXlfdHJpYWxzICVpbiUgbG9zc190cmlhbHMpKzFdCiAgCiAgcG9zdF9nYWluX3RyaWFscyA9IHBsYXlfdHJpYWxzW3doaWNoKHBsYXlfdHJpYWxzICVpbiUgZ2Fpbl90cmlhbHMpKzFdCiAgCiAgbnVtX3RyaWFsc19wb3N0X2xvc3MgPSBwb3N0X2xvc3NfdHJpYWxzIC0gbG9zc190cmlhbHMKICAKICBudW1fdHJpYWxzX3Bvc3RfZ2FpbiA9IHBvc3RfZ2Fpbl90cmlhbHMgLSBnYWluX3RyaWFscwogIAogIGlmKGxlbmd0aChudW1fdHJpYWxzX3Bvc3RfZ2Fpbik+bGVuZ3RoKG51bV90cmlhbHNfcG9zdF9sb3NzKSl7CiAgICBudW1fdHJpYWxzX3Bvc3RfbG9zcyA8LSBjKG51bV90cmlhbHNfcG9zdF9sb3NzLCByZXAoTkEsIGxlbmd0aChudW1fdHJpYWxzX3Bvc3RfZ2FpbikgLSBsZW5ndGgobnVtX3RyaWFsc19wb3N0X2xvc3MpKSkKICB9CiAgZWxzZSBpZihsZW5ndGgobnVtX3RyaWFsc19wb3N0X2dhaW4pPGxlbmd0aChudW1fdHJpYWxzX3Bvc3RfbG9zcykpewogICAgbnVtX3RyaWFsc19wb3N0X2dhaW4gPC0gYyhudW1fdHJpYWxzX3Bvc3RfZ2FpbiwgcmVwKE5BLCBsZW5ndGgobnVtX3RyaWFsc19wb3N0X2xvc3MpIC0gbGVuZ3RoKG51bV90cmlhbHNfcG9zdF9nYWluKSkpCiAgfQogIAogIHJldHVybihkYXRhLmZyYW1lKG51bV90cmlhbHNfcG9zdF9sb3NzID0gbnVtX3RyaWFsc19wb3N0X2xvc3MsIG51bV90cmlhbHNfcG9zdF9nYWluID0gbnVtX3RyaWFsc19wb3N0X2dhaW4pKQp9CmBgYAoKVGhlIHBsb3QgYmVsb3cgc2hvd3MgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHRyaWFscyBpdCB0YWtlcyBhIHN1YmplY3QgdG8gcGxheSBhIGdpdmVuIG1hY2hpbmUgYWZ0ZXIgZXhwZXJpZW5jaW5nIGEgbG9zcyBvciBhIGdhaW4uICAgCgpGb3IgZXZlcnlvbmUgYW5kIGZvciBldmVyeSBtYWNoaW5lIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0cmlhbHMgaXQgdGFrZXMgYSBzdWJqZWN0IHRvIHBsYXkgZm9sbG93aW5nIGEgbG9zcyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSBudW1iZXIgb2YgdHJpYWxzIGl0IHRha2UgdGhlbSB0byBwbGF5IGZvbGxvd2luZyBhIGdhaW4uIFRoaXMgc3VnZ2VzdHMgdGhhdCBzdWJqZWN0cyBhcmUgcmVzcG9uZGluZyB0byBvdXRjb21lcyBpbiBhIHdheSBvdmVyd2VpZ2h0cyB0aGVpciBtb3N0IHJlY2VudCBleHBlcmllbmNlIHdpdGggdGhlIG1hY2hpbmUuICAgCgpgYGB7cn0KdG1wID0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMpICU+JQogIGRvKGNvdW50LnBvc3RvdXRjb21lLnRyaWFscyguKSkgICU+JQogIGRvKGFzc2lnbi5hZ2UuaW5mbyguKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdChmYWNldF9sYWJlbHMsIGFnZV9ncm91cCwgbnVtX3RyaWFsc19wb3N0X2xvc3MsIG51bV90cmlhbHNfcG9zdF9nYWluLCBTdWJfaWQpICU+JQogIGdhdGhlcihrZXksIHZhbHVlLCAtZmFjZXRfbGFiZWxzLCAtYWdlX2dyb3VwLCAtU3ViX2lkKSAlPiUKICBtdXRhdGUoa2V5ID0gZ3N1YigibnVtX3RyaWFsc19wb3N0XyIsICIiLCBrZXkpKSAKCnRtcCAlPiUKICBncm91cF9ieShmYWNldF9sYWJlbHMsIGFnZV9ncm91cCwga2V5KSAlPiUKICBzdW1tYXJpc2UobWVhbl9wb3N0ID0gbWVhbih2YWx1ZSwgbmEucm09VCksCiAgICAgICAgICAgIHNlbV9wb3N0ID0gc2VtKHZhbHVlKSkgJT4lCiAgZ2dwbG90KGFlcyhhZ2VfZ3JvdXAsIG1lYW5fcG9zdCwgc2hhcGU9a2V5LCBjb2w9YWdlX2dyb3VwKSkrCiAgZ2VvbV9wb2ludChzaXplPTIpKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtZWFuX3Bvc3Qtc2VtX3Bvc3QsIHltYXggPSBtZWFuX3Bvc3Qrc2VtX3Bvc3QpLCB3aWR0aD0wLCBzaXplPTIpKwogIGZhY2V0X3dyYXAofmZhY2V0X2xhYmVscykrCiAgeGxhYigiTnVtYmVyIG9mIHRyaWFscyB1bnRpbCBuZXh0IHBsYXkiKSsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpKwogIGd1aWRlcyhjb2xvcj1GQUxTRSkKYGBgCgpSZWZsZWN0aW5nIHRoZSBnbG9iYWwgYmVoYXZpb3IgaW4gcHJvcG9ydGlvbiBvZiBwbGF5aW5nIGluIGVhY2ggY29uZGl0aW9uIGFkdWx0cyB0YWtlIGxvbmdlciB0byBwbGF5IGFmdGVyIGxhcmdlIGxvc3NlcyBpbiB0aGUgaGlnaCB2YXJpYW5jZSBuZWdhdGl2ZSBFViBjb25kaXRpb24gY29tcGFyZWQgdG8ga2lkcyB3aGlsZSBraWRzIGFyZSBsZXNzIHNlbnNpdGl2ZSB0byB0aGUgbWFnbml0dWRlIG9mIGxvc3MuCgpgYGB7cn0Kc3VtbWFyeShsbSh2YWx1ZX5hZ2VfZ3JvdXAqZmFjZXRfbGFiZWxzLHRtcCAlPiVmaWx0ZXIoa2V5PT0ibG9zcyIpKSkKYGBgCgoqT25lIHRob3VnaHQgdGhhdCBpcyBub3QgbmVjZXNzYXJpbHkgaW1tZWRpYXRlbHkgcGVydGluZW50IGJ1dCB0aGF0IEkgcHV6emxlZCBvdmVyIGlzIGhvdyB0aGlzIGdyYXBoIHdvdWxkIGhhdmUgbG9va2VkIGxpa2UgaWYgc3ViamVjdHMgd2VyZSB0YWtpbmcgYWxsIHRoZWlyIGV4cGVyaWVuY2VzIHdpdGggdGhlIG1hY2hpbmUgaW4gdG8gYWNjb3VudCAoaW5zdGVhZCBvZiBvdmVyd2VpZ2hpbmcgdGhlaXIgbW9zdCByZWNlbnQgZXhwZXJpZW5jZSkuIEkgaGF2ZSBhIHZhZ3VlIGludHVpdGlvbiB0aGF0IGluIHRoYXQgY2FzZSB0aGUgZGlmZmVyZW5jZSBpbiByZXNwb25kaW5nIGJldHdlZW4gdGhlIGV4cGVyaWVuY2VzIChnYWluL2xvc3MpIHdvdWxkIGJlIDAuIFRoYXQgaXMsIGlmIG9uZSB0YWtlcyBpbiB0byBhY2NvdW50IGFsbCB0aGVpciBleHBlcmllbmNlcyB0aGVuIHRoZXkgd291bGQgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIEVWIG1hY2hpbmVzIGFuZCBlaXRoZXIgYWx3YXlzIHBsYXkgZm9yIHBvc2l0aXZlIEVWIG1hY2hpbmVzIG9yIG5ldmVyIHBsYXkgZm9yIG5lZ2F0aXZlIEVWIG1hY2hpbmVzIHJlZ2FyZGxlc3Mgb2YgdGhlIG9ic2VydmVkIG91dGNvbWUuIFJlbGF0ZWRseSB0aGVuLCB0aGlzIGRpZmZlcmVuY2UgaW4gcmVzcG9uc2UgcGF0dGVybnMgZGVwZW5kaW5nIG9uIHRoZSBvYnNlcnZlZCBvdXRjb21lIGNvdWxkIGJlIGR1ZSB0byBhdCBsZWFzdCB0d28gcmVhc29uczogbWVtb3J5IG9yIGxvc3MgYXZlcnNpb24uIE9yIHBlcmhhcHMgc3Ryb25nZXIgbWVtb3JpZXMgZm9yIGxvc3Nlcy4gSSdtIG5vdCBzdXJlIHdoZXJlIEknbSBnb2luZyB3aXRoIHRoaXMgYnV0IHBlcmhhcHMgdGhlcmUgaXMgc29tZXRoaW5nIGludGVyZXN0aW5nIHRvIGxvb2sgYXQgaW4gdGhlIGhpcHBvY2FtcGFsIGFjdGl2aXR5IGZvbGxvd2luZyBsb3NzZXMgdmVyc3VzIGdhaW5zLioKCiMjIyBDcm9zcy10YWxrIGJldHdlZW4gbWFjaGluZXMKCkFyZSBzdWJqZWN0cyBsZXNzIGxpa2VseSB0byBwbGF5IG92ZXJhbGwgYWZ0ZXIgYSBsb3NzIG9yIG9ubHkgbGVzcyBsaWtlbHkgdG8gcGxheSB0aGF0IG1hY2hpbmUgYWZ0ZXIgYSBsb3NzIGZvciB0aGF0IG1hY2hpbmU/CgpgYGB7cn0KbWVhbi5wb3N0bG9zcy5wbGF5LnByb2IgPC0gZnVuY3Rpb24oc3ViamVjdF9kYXRhKXsKICAKICBTdWJfaWQgPSB1bmlxdWUoc3ViamVjdF9kYXRhJFN1Yl9pZCkKICAKICBsb3NzX3RyaWFscyA9IHdoaWNoKHN1YmplY3RfZGF0YSRQb2ludHNfZWFybmVkPDApCiAgCiAgbWVhbl9wb3N0X2xvc3NfcHJvYiA8LSBtZWFuKGlmZWxzZShzdWJqZWN0X2RhdGEkUmVzcG9uc2VbbG9zc190cmlhbHMrMV0gPT0gInBsYXkiLCAxLCAwKSwgbmEucm09VCkKICAKICByZXR1cm4oZGF0YS5mcmFtZShtZWFuX3Bvc3RfbG9zc19wcm9iPW1lYW5fcG9zdF9sb3NzX3Byb2IpKQp9CmBgYAoKUHJvYmFiaWxpdHkgb2YgcGxheWluZyBmb2xsb3dpbmcgYSBsb3NzIGRlcGVuZHMgb24gbWFjaGluZSB0eXBlLiBMb29raW5nIGF0IGFsbCB0cmlhbHMgbWFza3MgdGhpcyBkaWZmZXJlbmNlLiBTdWJqZWN0cyBzZWVtIHRvIGxlYXJuIG1hY2hpbmUgc3BlY2lmaWNhbGx5IGFuZCBjcm9zcy10YWxrIGlzbid0IGV2aWRlbnQgaGVyZS4KCmBgYHtyfQp0bXAgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiAlPiUKICBncm91cF9ieShTdWJfaWQpICU+JQogIGRvKG1lYW4ucG9zdGxvc3MucGxheS5wcm9iKC4pKSAlPiUKICBtdXRhdGUoZmFjZXRfbGFiZWxzID0gImFsbF90cmlhbHMiKQoKbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoU3ViX2lkLCBmYWNldF9sYWJlbHMpICU+JQogIGRvKG1lYW4ucG9zdGxvc3MucGxheS5wcm9iKC4pKSAlPiUKICByYmluZCh0bXApICU+JQogIGRvKGFzc2lnbi5hZ2UuaW5mbyguKSkgJT4lCiAgZ3JvdXBfYnkoYWdlX2dyb3VwLCBmYWNldF9sYWJlbHMpICU+JQogIHN1bW1hcmlzZShtcCA9IG1lYW4obWVhbl9wb3N0X2xvc3NfcHJvYixuYS5ybT1UKSwKICAgICAgICAgICAgc3AgPSBzZW0obWVhbl9wb3N0X2xvc3NfcHJvYikpICU+JQogIGdncGxvdChhZXMoZmFjZXRfbGFiZWxzLCBtcCwgZmlsbD1hZ2VfZ3JvdXApKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIscG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkrCiAgZ2VvbV9lcnJvcmJhcih3aWR0aD0wLCBhZXMoeW1pbiA9IG1wLXNwLCB5bWF4ID0gbXArc3ApLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSkpKwogIHhsYWIoIiIpKwogIHlsYWIoIlBvc3QgbG9zcyBwbGF5IHByb2JhYmlsaXR5IikrCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyBSZXNwb25zZSB0aW1lIGRpZmZlcmVuY2VzCgpgYGB7cn0KbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4gJT4lCiAgZ2dwbG90KGFlcyhsb2coUmVhY3Rpb25fdGltZSkpKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsID0gYWdlX2dyb3VwKSwgYWxwaGE9MC41LCBjb2xvcj1OQSkgKwogIGZhY2V0X3dyYXAofmZhY2V0X2xhYmVscykrCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSsKICB5bGFiKCIiKSsKICB4bGFiKCJMb2cgUmVzcG9uc2UgVGltZSIpCmBgYAoKYGBge3J9Cm1hY2hpbmVfZ2FtZV9kYXRhX2NsZWFuICU+JQogIGdyb3VwX2J5KFN1Yl9pZCwgZmFjZXRfbGFiZWxzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9sb2dfcnQgPSBtZWFuKGxvZyhSZWFjdGlvbl90aW1lKSksCiAgICAgICAgICAgIHNlbV9sb2dfcnQgPSBzZW0obG9nKFJlYWN0aW9uX3RpbWUpKSkgJT4lCiAgZG8oYXNzaWduLmFnZS5pbmZvKC4pKSAlPiUKICBnZ3Bsb3QoYWVzKGFnZV9ncm91cCwgbWVhbl9sb2dfcnQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9YWdlX2dyb3VwKSkrCiAgZmFjZXRfd3JhcCh+ZmFjZXRfbGFiZWxzKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKwogIHlsYWIoIk1lYW4gTG9nIFJ0IikrCiAgeGxhYigiQWdlIGdyb3VwIikKYGBgCgpCb3RoIHRlZW5zIGFuZCBhZHVsdHMgYXJlIGZhc3RlciB0aGFuIGtpZHMgaW4gYWxsIGNvbmRpdGlvbnMgYnV0IHRoZSBoaWdoIHZhciBuZWdhdGl2ZSBFVi4KCmBgYHtyfQojc3VtbWFyeShsbWVyKGxvZyhSZWFjdGlvbl90aW1lKSB+IGFnZV9ncm91cCpmYWNldF9sYWJlbHMgKygxfFN1Yl9pZCksIGRhdGEgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbikpCgpzdW1tYXJ5KGxtZXIobG9nKFJlYWN0aW9uX3RpbWUpIH4gYWdlX2dyb3VwICsoMXxTdWJfaWQpLCBkYXRhID0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4lPiVmaWx0ZXIoZmFjZXRfbGFiZWxzID09ICItMTAsKzEwMCIpKSkKCnN1bW1hcnkobG1lcihsb2coUmVhY3Rpb25fdGltZSkgfiBhZ2VfZ3JvdXAgKygxfFN1Yl9pZCksIGRhdGEgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiU+JWZpbHRlcihmYWNldF9sYWJlbHMgPT0gIi01LCs0OTUiKSkpCgpzdW1tYXJ5KGxtZXIobG9nKFJlYWN0aW9uX3RpbWUpIH4gYWdlX2dyb3VwICsoMXxTdWJfaWQpLCBkYXRhID0gbWFjaGluZV9nYW1lX2RhdGFfY2xlYW4lPiVmaWx0ZXIoZmFjZXRfbGFiZWxzID09ICIrMTAsLTEwMCIpKSkKCnN1bW1hcnkobG1lcihsb2coUmVhY3Rpb25fdGltZSkgfiBhZ2VfZ3JvdXAgKygxfFN1Yl9pZCksIGRhdGEgPSBtYWNoaW5lX2dhbWVfZGF0YV9jbGVhbiU+JWZpbHRlcihmYWNldF9sYWJlbHMgPT0gIis1LC00OTUiKSkpCmBgYAoKIyMgUkwgbW9kZWxpbmcKCkRldGFpbHMgb2YgbW9kZWwgY29tcGFyaXNvbiBjYW4gYmUgZm91bmQgaW4gYSBzZXBhcmF0ZSBbbm90ZWJvb2tdKGh0dHA6Ly96ZW5rYXZpLmdpdGh1Yi5pby9EZXZTdHVkeV9BbmFseXNlcy9vdXRwdXQvcmVwb3J0cy9Db21wX1JMLm5iLmh0bWwpLgoKQmFzZWQgb24gYSBjb21wYXJpc29uIG9mIDYgbW9kZWxzIHRoZSBiZXN0IGZpdHRpbmcgbW9kZWwgaXMgYSA0IHBhcmFtZXRlciBtb2RlbCB3aXRoIG9uZSBsZWFybmluZyByYXRlICgkXGFscGhhJCkgYW5kIHR3byBleHBvbmVudHMgKCRcZ2FtbWEkKSB3ZWlnaGluZyBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgcHJlZGljdGlvbiBlcnJvcnMgc2VwYXJhdGVseSAoYWxvbmcgd2l0aCB0aGUgaW52ZXJzZSBzb2Z0bWF4IHRlbXBlcmF0dXJlICRcYmV0YSQgKS4KCkluIGxpbmUgd2l0aCB0aGUgYWJvdmUgYW5hbHlzZXMgd2hlcmUgdGhlIGJlaGF2aW9yYWwgZGlmZmVyZW5jZSBsYXkgaW4gdGhlIGhpZ2ggdmFyaWFuY2UgbmVnYXRpdmUgRVYgbWFjaGluZSB0aGUgYWdlIGRpZmZlcmVuY2UgaW4gdGhlIHBhcmFtZXRlcnMgaXMgZm91bmQgaW46CgoxLiBleHBvbmVudCBvdmVyIG5lZ2F0aXZlIHByZWRpY3Rpb24gZXJyb3JzOiAqKkFkdWx0cyBkaXN0b3J0IG5lZ2F0aXZlIG91dGNvbWVzIGxlc3MuIEEgbmVnYXRpdmUgb3V0Y29tZSBkb2Vzbid0IGZlZWwgYXMgYmFkIGFzIGl0IGlzIGZvciBraWRzKiogCgoyLiBpbnZlcnNlIHNvZnRtYXggdGVtcGVyYXR1cmU6ICoqQWR1bHRzIG1ha2UgZGVjaXNpb25zIGJhc2VkIG1vcmUgb24gRVYgY29tcGFyZWQgdG8ga2lkcyoqCgpUaGVyZWZvcmUgdGhlIHRyaWFsIGxldmVsIFBFcyBmcm9tIHRoaXMgbW9kZWwgd2lsbCBiZSB1c2VkIGFzIGEgcGFyYW1ldHJpYyByZWdyZXNzb3IgZm9yIHRoZSBpbWFnaW5nIGFuYWx5c2VzLgo=